接入Tradplus

This commit is contained in:
yuqian 2026-01-08 13:46:03 +08:00
commit ac17cad2cd
190 changed files with 31567 additions and 0 deletions

18
.gitignore vendored Normal file
View File

@ -0,0 +1,18 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
.idea/
.safedk/
app/debug/

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

BIN
app/Intelligent Board Normal file

Binary file not shown.

177
app/build.gradle.kts Normal file
View File

@ -0,0 +1,177 @@
import java.util.Date
import java.text.SimpleDateFormat
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
kotlin("kapt")
id ("kotlin-android")
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
}
val timestamp = SimpleDateFormat("MM_dd_HH_mm").format(Date())
android {
namespace = "com.app.intelligent.board"
compileSdk = 36
defaultConfig {
applicationId = "com.app.intelligent.board"
minSdk = 24
targetSdk = 36
versionCode = 2
versionName = "2.0"
setProperty(
"archivesBaseName",
"intelligen Board_V" + versionName + "(${versionCode})_$timestamp"
)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
buildConfig = true
viewBinding = true
}
}
dependencies {
implementation("androidx.core:core-ktx:1.15.0")
implementation("androidx.appcompat:appcompat:1.7.1")
implementation("com.google.android.material:material:1.13.0")
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
implementation("androidx.activity:activity:1.12.1")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.3.0")
androidTestImplementation("androidx.test.espresso:espresso-core:3.7.0")
implementation("com.squareup.okhttp3:okhttp:5.3.2")
implementation("com.github.bumptech.glide:glide:5.0.5")
implementation ("jp.wasabeef:glide-transformations:4.3.0")
//Glide支持webp动图的库
implementation("com.github.zjupure:webpdecoder:2.7.4.16.0")
implementation("com.github.omicronapps:7-Zip-JBinding-4Android:Release-16.02-2.03")
val room_version = "2.8.4"
implementation ("androidx.room:room-runtime:$room_version")
kapt("androidx.room:room-compiler:$room_version")
implementation ("androidx.room:room-ktx:$room_version")
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2")
implementation("com.google.android.material:material:1.8.0")
implementation ("androidx.core:core:1.12.0") // 用最新版本
// 其他依赖如glide的注解处理器也要补全
annotationProcessor ("com.github.bumptech.glide:compiler:4.16.0")
implementation(files("libs/TradPlusLibrary_01_04_12_20-release.aar"))
implementation(files("libs/UpLoadLibrary_12_03_15_13-release.aar"))
implementation ("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
implementation ("com.google.android.gms:play-services-ads-identifier:18.0.1")
implementation(platform("com.google.firebase:firebase-bom:33.7.0"))
implementation("com.google.firebase:firebase-crashlytics")
implementation("com.google.firebase:firebase-analytics")
implementation("com.google.firebase:firebase-config")
// TradPlus
implementation("com.tradplusad:tradplus:15.2.0.1")
implementation("androidx.legacy:legacy-support-v4:1.0.0")
implementation("androidx.appcompat:appcompat:1.3.0-alpha02")
// IronSource
implementation("com.ironsource.sdk:mediationsdk:9.0.0")
implementation("com.tradplusad:tradplus-ironsource:10.15.2.0.1")
// Pangle
implementation("com.tradplusad:tradplus-pangle:19.15.2.0.1")
implementation("com.pangle.global:pag-sdk:7.8.0.7")
// UnityAds
implementation("com.tradplusad:tradplus-unity:5.15.2.0.1")
implementation("com.unity3d.ads:unity-ads:4.16.3")
// Chartboost
// implementation("com.tradplusad:tradplus-chartboostx:15.15.2.0.1")
// implementation("com.chartboost:chartboost-sdk:9.10.0")
// implementation("com.google.android.gms:play-services-ads-identifier:17.0.0")
// implementation("com.google.android.gms:play-services-base:17.4.0")
//上面新版本下载失败用旧版本
implementation("com.tradplusad:tradplus-chartboostx:15.14.5.0.1")
implementation("com.chartboost:chartboost-sdk:9.8.3")
implementation("com.google.android.gms:play-services-ads-identifier:17.0.0")
implementation("com.google.android.gms:play-services-base:17.4.0")
// InMobi
implementation("com.tradplusad:tradplus-inmobix:23.15.2.0.1")
implementation("com.inmobi.monetization:inmobi-ads-kotlin:11.0.0")
implementation("com.squareup.okhttp3:okhttp:3.14.9")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
implementation("androidx.core:core-ktx:1.5.0")
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.0")
implementation("com.google.android.gms:play-services-ads-identifier:18.0.1")
implementation("com.google.android.gms:play-services-location:21.0.1") // optional
implementation("androidx.browser:browser:1.8.0")
implementation("com.squareup.picasso:picasso:2.8")
implementation("androidx.viewpager:viewpager:1.0.0")
implementation("androidx.recyclerview:recyclerview:1.2.1")
// Fyber
implementation("com.fyber:marketplace-sdk:8.4.0")
implementation("com.tradplusad:tradplus-fyber:24.15.2.0.1")
implementation("com.google.android.gms:play-services-ads-identifier:17.0.0")
implementation("com.google.android.gms:play-services-base:17.4.0")
// Mintegral
implementation("com.tradplusad:tradplus-mintegralx_overseas:18.15.2.0.1")
implementation("androidx.recyclerview:recyclerview:1.1.0")
implementation("com.mbridge.msdk.oversea:mbridge_android_sdk:16.10.11")
// Liftoff (Vungle)
implementation("com.tradplusad:tradplus-vunglex:7.15.2.0.1")
implementation("com.vungle:vungle-ads:7.6.0")
// Bigo
implementation("com.bigossp:bigo-ads:5.5.2")
implementation("com.tradplusad:tradplus-bigo:57.15.2.0.1")
// Cross Promotion
implementation("com.tradplusad:tradplus-crosspromotion:27.15.2.0.1")
// TP Exchange注意与主包版本同步
implementation("com.google.code.gson:gson:2.8.6")
implementation("com.tradplusad:tp_exchange:40.15.2.0.1")
// Google UMP
implementation ("com.google.android.ump:user-messaging-platform:3.2.0")
// TradPlus Tools
// implementation 'com.tradplusad:tradplus-tool:1.1.4'
}

29
app/google-services.json Normal file
View File

@ -0,0 +1,29 @@
{
"project_info": {
"project_number": "104297196916",
"project_id": "intelligent-board-f136c",
"storage_bucket": "intelligent-board-f136c.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:104297196916:android:88070a67df4fa316db1021",
"android_client_info": {
"package_name": "com.app.intelligent.board"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyA31HjdZd6hGBzfOhp2gzq2cloD6qISCaI"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

Binary file not shown.

Binary file not shown.

45
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,45 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
# 保持 Room 的核心类不被混淆
-keep class androidx.room.** { *; }
-keep class androidx.sqlite.db.** { *; }
# 保持 Room 数据库类的基本结构
-keep class * extends androidx.room.RoomDatabase { *; }
# 保持 Room DAO 接口
-keep @androidx.room.Dao interface * { *; }
-keep @androidx.room.Dao class * { *; }
# 保持 Room 实体类
-keep @androidx.room.Entity class * { *; }
# 保持 Room 的注解类
-keep @androidx.room.Database class * { *; }
-keep class com.omicronapplications.** { *; }
-keep class net.sf.sevenzipjbinding.** { *; }

View File

@ -0,0 +1,37 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.app.intelligent.board",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 2,
"versionName": "2.0",
"outputFile": "intelligen Board_V2.0(2)_01_08_11_48-release.apk"
}
],
"elementType": "File",
"baselineProfiles": [
{
"minApi": 28,
"maxApi": 30,
"baselineProfiles": [
"baselineProfiles/1/intelligen Board_V2.0(2)_01_08_11_48-release.dm"
]
},
{
"minApi": 31,
"maxApi": 2147483647,
"baselineProfiles": [
"baselineProfiles/0/intelligen Board_V2.0(2)_01_08_11_48-release.dm"
]
}
],
"minSdkVersionForDexing": 24
}

View File

@ -0,0 +1,25 @@
package com.keyboard.skinning.cool
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.ba.ali.apps.keyboard", appContext.packageName)
}
}

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.AD_ID" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<application
android:name=".AppContextHelper"
android:allowBackup="true"
android:icon="@mipmap/logo13"
android:label="@string/app_name"
android:roundIcon="@mipmap/logo13"
android:supportsRtl="true"
tools:replace="android:networkSecurityConfig"
android:networkSecurityConfig="@xml/net"
android:theme="@style/MyKeyBoard"
tools:targetApi="31">
<activity
android:name=".uiactivity.CategoryItemListActivity"
android:exported="false" />
<activity
android:name=".uiactivity.StartActivity"
android:exported="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".uiactivity.KeyboardSettingActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".uiactivity.OperateSuccessActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".uiactivity.CategoryMainActivity"
android:exported="true"
android:launchMode="singleTask"
android:screenOrientation="portrait" />
<activity
android:name=".uiactivity.SearchActivity"
android:exported="false"
/>
<service
android:name=".uikeyboard.KeyboardCustomInputMethodService"
android:exported="true"
android:permission="android.permission.BIND_INPUT_METHOD">
<intent-filter>
<action android:name="android.view.InputMethod" />
</intent-filter>
<meta-data
android:name="android.view.im"
android:resource="@xml/keyborad_xml" />
</service>
</application>
</manifest>

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,115 @@
package com.app.intelligent.board
import android.app.Application
import android.graphics.Typeface
import com.app.intelligent.board.uimodel.KeyboardItemInfo
import com.app.intelligent.board.uimodel.KeyboardItemDataWrapper
import com.up.uploadlibrary.UpLoadManager
import org.json.JSONArray
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.io.StringWriter
class AppContextHelper : Application() {
companion object {
lateinit var appInstance: AppContextHelper
lateinit var list: MutableList<KeyboardItemDataWrapper>
const val TAG = "AppContextHelper"
var defaultFont: Typeface? = null
const val DB_VERSION = 1
const val DB_NAME = "db_name"
// 关键:添加@JvmField让Java可访问声明为public
@JvmField
var trendingKeyboards: MutableList<KeyboardItemInfo> = mutableListOf()
}
override fun onCreate() {
super.onCreate()
appInstance = this
defaultFont = Typeface.createFromAsset(assets, "my_font.ttf")
UpLoadManager.init(this, TAG, { s: String?, s2: String? -> null })
dealFile()
}
private fun dealFile() {
val openFile = appInstance.assets.open("new_res.json")
val jsonString = getJsonString(openFile)
if (jsonString != null) {
resolveJsonString(jsonString)
}
}
private fun resolveJsonString(string: String) {
val jsonData = JSONArray(string)
var dataList: MutableList<KeyboardItemDataWrapper> = mutableListOf()
for (i in 0 until jsonData.length()) {
jsonData.getJSONObject(i).run {
val pName = getString("parent_name")
val listArray = getJSONArray("keyboard_list")
var beanDetailsList: MutableList<KeyboardItemInfo> = mutableListOf()
for (listIndex in 0 until listArray.length()) {
listArray.getJSONObject(listIndex).also {
val title = it.getString("title")
val thUrl = it.getString("thumbUrl")
val thGif = it.getString("thumbUrlGif")
var zipPath = ""
var imgPath = ""
var imgGif = ""
var imgPreviewGif = ""
if (it.has("detail")) {
val contentObject =
it.getJSONObject("detail").getJSONObject("themeContent")
zipPath = contentObject.getString("androidRawZipUrl")
imgPath = contentObject.getString("img")
imgGif = contentObject.getString("imgGif")
imgPreviewGif = contentObject.getString("imgPreviewGif")
}
beanDetailsList.add(
KeyboardItemInfo().apply {
setImgPath(imgPath)
setZipPath(zipPath)
setTitleName(title)
setImgGif(imgGif)
thumbUrl = thUrl
thumbGif = thGif
})
}
}
val shuffled = beanDetailsList.shuffled()
val dataBeanWrapper = KeyboardItemDataWrapper()
.apply {
parentName = pName
keyboardList = shuffled
}
dataList.add(dataBeanWrapper)
}
}
updateDataList(dataList)
}
private fun updateDataList(mainList: MutableList<KeyboardItemDataWrapper>) {
list = mainList
}
private fun getJsonString(fileInputStream: InputStream): String? {
return try {
val charArray = CharArray(fileInputStream.available())
var readCount = 0
val streamReader = InputStreamReader(fileInputStream)
val bufferedReader = BufferedReader(streamReader)
val stringWriter = StringWriter()
while (bufferedReader.read(charArray).also { readCount = it } != -1) {
stringWriter.write(charArray, 0, readCount)
}
stringWriter.toString()
} catch (e: IOException) {
""
}
}
}

View File

@ -0,0 +1,76 @@
package com.app.intelligent.board.uiactivity;
import android.os.Bundle;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.ad.tradpluslibrary.TPAdManager;
import com.app.intelligent.board.AppContextHelper;
import com.app.intelligent.board.R;
import com.app.intelligent.board.uimodel.KeyboardItemInfo;
import com.app.intelligent.board.uimodel.KeyboardItemDataWrapper;
import com.app.intelligent.board.databinding.ActivityCategoryItemListBinding;
import com.app.intelligent.board.uiadapter.HomeItemSubAdapter;
import com.app.intelligent.board.utils.KeyboardRvItemDecoration;
import java.util.List;
public class CategoryItemListActivity extends AppCompatActivity {
private ActivityCategoryItemListBinding vb;
public static final String KEY_NAME ="class_name";
private String name;
private List<KeyboardItemInfo> data;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
vb = ActivityCategoryItemListBinding.inflate(getLayoutInflater());
EdgeToEdge.enable(this);
setContentView(vb.getRoot());
TPAdManager.INSTANCE.showTPAD(this,()->null);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
name = getIntent().getStringExtra(KEY_NAME);
initData();
initClick();
}
private void initData(){
vb.className.setText(name);
for (KeyboardItemDataWrapper beanWrapper : AppContextHelper.list) {
if(beanWrapper.getParentName().equals(name)){
data = beanWrapper.getKeyboardList();
}
}
KeyboardRvItemDecoration listDecoration = new KeyboardRvItemDecoration(15, 15, 0);
HomeItemSubAdapter adapterMain = new HomeItemSubAdapter(this,data);
// 核心修改自定义GridLayoutManager
vb.recycler.setLayoutManager(new GridLayoutManager(CategoryItemListActivity.this, 1) {
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
RecyclerView.LayoutParams params = super.generateDefaultLayoutParams();
int targetHeight = (int) (240 * getResources().getDisplayMetrics().density);
params.height = targetHeight;
return params;
}
});
vb.recycler.setAdapter(adapterMain);
vb.recycler.addItemDecoration(listDecoration);
}
private void initClick(){
vb.back.setOnClickListener(v -> finish());
}
}

View File

@ -0,0 +1,147 @@
package com.app.intelligent.board.uiactivity;
import android.graphics.PorterDuff;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.Fragment;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import com.app.intelligent.board.R;
import com.app.intelligent.board.databinding.ActivityCategoryMainBinding;
import com.app.intelligent.board.uifragment.FavoritesKeyboardFragment;
import com.app.intelligent.board.uifragment.HomeMainFragment;
import com.google.android.material.tabs.TabLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class CategoryMainActivity extends AppCompatActivity {
private ActivityCategoryMainBinding vb;
// 定义Tab图标资源ID未选中态
private final int[] tabIcons = {R.drawable.home3, R.drawable.favorites3};
// 定义Tab对应的文字
private final String[] tabTexts = {"Home", "Favorites"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
vb = ActivityCategoryMainBinding.inflate(getLayoutInflater());
EdgeToEdge.enable(this);
setContentView(vb.getRoot());
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
init();
}
private void init() {
List<Fragment> listFragment = new ArrayList<>();
listFragment.add(HomeMainFragment.newInstance());
listFragment.add(FavoritesKeyboardFragment.newInstance());
// 配置TabLayout显示模式
vb.tabLayout.setTabMode(TabLayout.MODE_FIXED);
vb.tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
for (int i = 0; i < listFragment.size(); i++) {
TabLayout.Tab tab = vb.tabLayout.newTab();
View inflate = LayoutInflater.from(this).inflate(R.layout.layout_tab_custom, null, false);
ImageView icon = inflate.findViewById(R.id.im_icon);
TextView textView = inflate.findViewById(R.id.textView);
textView.setText(tabTexts[i]); // 设置Tab文字
if (i == 0) {
// 首页Tab - 默认选中只显示黑色文字隐藏图标
icon.setVisibility(View.GONE);
textView.setVisibility(View.VISIBLE);
textView.setTextColor(getResources().getColor(R.color.black));
} else {
// 收藏Tab - 默认未选中只显示灰色图标隐藏文字
icon.setImageResource(tabIcons[i]);
// 给图标设置灰色滤镜核心将图标染成灰色
icon.setColorFilter(getResources().getColor(R.color.gray), PorterDuff.Mode.SRC_IN);
icon.setVisibility(View.VISIBLE);
textView.setVisibility(View.GONE);
}
tab.setCustomView(inflate);
vb.tabLayout.addTab(tab);
}
vb.viewpager.setUserInputEnabled(false);
vb.viewpager.setAdapter(new FragmentStateAdapter(this) {
@NonNull
@Override
public Fragment createFragment(int position) {
return listFragment.get(position);
}
@Override
public int getItemCount() {
return listFragment.size();
}
});
bindTabEvent();
}
private void bindTabEvent() {
vb.tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
View customView = Objects.requireNonNull(tab.getCustomView());
ImageView icon = customView.findViewById(R.id.im_icon);
TextView textView = customView.findViewById(R.id.textView);
// 选中状态隐藏图标显示黑色文字
icon.setVisibility(View.GONE);
textView.setVisibility(View.VISIBLE);
textView.setTextColor(getResources().getColor(R.color.dark_gray));
vb.viewpager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
View customView = Objects.requireNonNull(tab.getCustomView());
ImageView icon = customView.findViewById(R.id.im_icon);
TextView textView = customView.findViewById(R.id.textView);
// 未选中状态显示灰色图标隐藏文字
icon.setImageResource(tabIcons[tab.getPosition()]);
// 重置图标颜色为灰色
icon.setColorFilter(getResources().getColor(R.color.color_gray), PorterDuff.Mode.SRC_IN);
icon.setVisibility(View.VISIBLE);
textView.setVisibility(View.GONE);
}
@Override
public void onTabReselected(TabLayout.Tab tab) {}
});
vb.viewpager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
Objects.requireNonNull(vb.tabLayout.getTabAt(position)).select();
}
});
}
}

View File

@ -0,0 +1,388 @@
package com.app.intelligent.board.uiactivity
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.TextView
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.OnApplyWindowInsetsListener
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.widget.ViewPager2
import com.ad.tradpluslibrary.TPAdManager
import com.ad.tradpluslibrary.TPAdManager.showTPAD
import com.app.intelligent.board.AppContextHelper
import com.app.intelligent.board.R
import com.app.intelligent.board.uimodel.KeyboardItemInfo
import com.app.intelligent.board.uilistener.KeyboardItemClickListener
import com.app.intelligent.board.uilistener.KeyboardConfigChangeListener
import com.app.intelligent.board.uidb.MyAppDatabaseManager
import com.app.intelligent.board.uiadapter.KeyboardItemAdapter
import com.app.intelligent.board.uifragment.KeyboardActivationDialog
import com.app.intelligent.board.utils.AppCommonUtils
import com.app.intelligent.board.utils.KeyboardZipFileHandler
import com.app.intelligent.board.utils.KeyboardThemeSaver
import com.bumptech.glide.Glide
import com.bumptech.glide.integration.webp.decoder.WebpDrawable
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import kotlinx.coroutines.launch
import java.io.File
class KeyboardSettingActivity : AppCompatActivity() {
companion object {
@JvmField
var DISPLAY_URL_KEY: String = "display_url_key"
@JvmField
val ZIP_URL_KEY = "zip_url_key"
@JvmField
val NAME_KEY = "name_key"
@JvmField
val GIF_KEY = "gif_key"
@JvmField
val THUMB_KEY = "thumb_key"
@JvmField
val SOURCE_KEY = "data_key"
}
// 新增声明tv_favorite控件
private lateinit var tvFavorite: TextView
// 进度条指示器相关
private lateinit var progressIndicator: ProgressBar
private var totalPageCount = 0
private var dialog: KeyboardActivationDialog? = null
private lateinit var displayUrl: String
private lateinit var gifUrl: String
private lateinit var zipUrl: String
private lateinit var name: String
private lateinit var applyBtn: LinearLayout
private lateinit var imgData: ImageView
private lateinit var imgBack: ImageView
private lateinit var textName: TextView
private lateinit var recommendedViewPager: ViewPager2
private lateinit var loadingLayout: FrameLayout
private lateinit var unzipPath: String
private lateinit var tvDownload: TextView
private lateinit var imDownload: ImageView
private lateinit var thumb: String
private lateinit var imgLike: ImageView
private lateinit var layoutFavoriteApply: LinearLayout
private lateinit var data: KeyboardItemInfo
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_keyboard_setting)
this.enableEdgeToEdge()
showTPAD(this) {}
// 修复优化父布局Insets处理
ViewCompat.setOnApplyWindowInsetsListener(
findViewById<View?>(R.id.main),
OnApplyWindowInsetsListener { v: View?, insets: WindowInsetsCompat? ->
val systemBars = insets!!.getInsets(WindowInsetsCompat.Type.systemBars())
v!!.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
})
findViewId()
// 修复给ViewPager2单独设置Insets监听避免高版本系统栏影响
initViewPagerInsets()
getExtraData()
displayData()
setApply()
onClick()
initRecommendedViewPager()
}
// 新增ViewPager2的Insets适配
private fun initViewPagerInsets() {
ViewCompat.setOnApplyWindowInsetsListener(recommendedViewPager) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, 0, systemBars.right, 0)
insets
}
}
private fun getExtraData() {
data = intent.getSerializableExtra(SOURCE_KEY) as KeyboardItemInfo
displayUrl = intent.getStringExtra(DISPLAY_URL_KEY).toString()
zipUrl = intent.getStringExtra(ZIP_URL_KEY).toString()
name = intent.getStringExtra(NAME_KEY).toString()
gifUrl = intent.getStringExtra(GIF_KEY).toString()
thumb = intent.getStringExtra(THUMB_KEY).toString()
val serviceZipName = KeyboardZipFileHandler.getServiceZipName(zipUrl)
unzipPath = KeyboardZipFileHandler.getUnzipPath(serviceZipName)
Log.d("-------------------", "-------unzipPath=" + unzipPath)
lifecycleScope.launch {
MyAppDatabaseManager.getIsLike(name) { isLiked ->
// 核心修改:初始化时同步图标和文字状态
imgLike.isSelected = isLiked
updateFavoriteText(isLiked)
}
}
if (File(unzipPath).exists()) {
imDownload.isVisible = false
tvDownload.text = getString(R.string.apply)
} else {
imDownload.isVisible = true
tvDownload.text = getString(R.string.download_apply)
}
}
private fun findViewId() {
applyBtn = findViewById(R.id.layoutDownloadApply)
imgData = findViewById(R.id.image_data)
imgBack = findViewById(R.id.back)
textName = findViewById(R.id.textview_data_name)
recommendedViewPager = findViewById(R.id.recommended_viewpager)
imgLike = findViewById(R.id.im_like)
layoutFavoriteApply = findViewById(R.id.layoutFavoriteApply)
loadingLayout = findViewById(R.id.loading)
imDownload = findViewById(R.id.im_download)
tvDownload = findViewById(R.id.tv_download)
// 核心修改绑定tv_favorite控件
tvFavorite = findViewById(R.id.tv_favorite)
// 绑定进度条指示器
progressIndicator = findViewById(R.id.progress_indicator)
}
private fun displayData() {
textName.text = name
if (gifUrl.isNotEmpty()) {
loadImgGif()
} else {
Glide.with(this)
.load(displayUrl)
.thumbnail(Glide.with(this).load(thumb))
.into(imgData)
}
}
private fun onClick() {
imgBack.setOnClickListener {
finish()
}
layoutFavoriteApply.setOnClickListener {
val newState = !imgLike.isSelected
imgLike.isSelected = newState
// 核心修改:点击后同步文字状态
updateFavoriteText(newState)
lifecycleScope.launch {
if (newState) {
MyAppDatabaseManager.addLike(data)
} else {
MyAppDatabaseManager.removeLike(data)
}
}
}
}
// 核心新增统一更新Favorite文字的方法
private fun updateFavoriteText(isLiked: Boolean) {
tvFavorite.text = if (isLiked) {
getString(R.string.favorite_apply_selected) // 选中时显示Favorited
} else {
getString(R.string.favorite_apply) // 未选中时显示Favorite
}
}
// 初始化ViewPager2 + 进度条指示器(修复最后一页进度)
private fun initRecommendedViewPager() {
val forYouList = AppContextHelper.list.filter {
it.parentName == getString(R.string.recommend_name)
}
val allRecommendItems = forYouList[0].keyboardList.shuffled()
// 计算总页数2行2列每页4个item
val itemsPerPage = 2 * 2
totalPageCount = (allRecommendItems.size + itemsPerPage - 1) / itemsPerPage
// 设置ViewPager2适配器
recommendedViewPager.adapter = KeyboardItemAdapter(
this,
allRecommendItems,
itemClickCallback = object : KeyboardItemClickListener {
override fun OnItemClickListener() {
finish()
}
}
)
// 监听页面滑动实时更新进度条最后一页强制100%
recommendedViewPager.registerOnPageChangeCallback(object :
ViewPager2.OnPageChangeCallback() {
// 页面滑动过程中(实时更新进度)
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
// 最后一页强制进度100%
if (position == totalPageCount - 1) {
progressIndicator.progress = 100
return
}
// 非最后一页:正常计算进度
val totalProgress = ((position + positionOffset) / totalPageCount) * 100
progressIndicator.progress = totalProgress.toInt()
}
// 页面选中后(校准进度)
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
// 最后一页进度100%;其他页:按比例计算
val progress = if (position == totalPageCount - 1) {
100
} else {
((position.toFloat() / totalPageCount) * 100).toInt()
}
progressIndicator.progress = progress
}
})
// 初始化进度条默认0%
progressIndicator.progress = 0
}
@SuppressLint("CheckResult")
private fun loadImgGif() {
Glide.with(this)
.load(gifUrl)
.thumbnail(Glide.with(this).load(thumb))
.addListener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>,
isFirstResource: Boolean
): Boolean {
return false
}
override fun onResourceReady(
resource: Drawable,
model: Any,
target: Target<Drawable>?,
dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
if (resource is WebpDrawable) {
resource.loopCount = WebpDrawable.LOOP_FOREVER
}
return false
}
}).into(imgData)
}
private fun setApply() {
applyBtn.setOnClickListener {
showTPAD(this) {
val checkEnable = AppCommonUtils.checkEnable(this)
val checkSetDefault = AppCommonUtils.checkSetDefault(this)
if (!checkEnable || !checkSetDefault) {
showDialog()
return@showTPAD
}
startDown()
}
}
}
private fun showDialog() {
dialog = dialog ?: KeyboardActivationDialog.newInstance().apply {}
dialog?.setClickListener {
startDown()
}
dialog?.show(supportFragmentManager, "")
}
private fun startDown() {
loadingLayout.isVisible = true
applyBtn.isEnabled = false
val file = File(unzipPath)
if (file.exists()) {
val findFirstDirectory = KeyboardZipFileHandler.findFirstDirectory(file)
apply("${findFirstDirectory}/")
applyBtn.isEnabled = true
loadingLayout.isVisible = false
} else {
KeyboardZipFileHandler.startDownloadZip(zipUrl, object :
KeyboardConfigChangeListener {
override fun OnApplySkinListener(fileList: List<File?>?) {
runOnUiThread {
applyBtn.isEnabled = true
loadingLayout.isVisible = false
}
if (fileList.isNullOrEmpty()) {
runOnUiThread {
Toast.makeText(
this@KeyboardSettingActivity,
getString(R.string.download_fail),
Toast.LENGTH_SHORT
).show()
}
} else {
if (file.exists()) {
val findFirstDirectory = KeyboardZipFileHandler.findFirstDirectory(file)
Log.d(
AppContextHelper.TAG,
"----apply------------it=$findFirstDirectory"
)
runOnUiThread {
apply("${findFirstDirectory}/")
}
}
}
}
})
}
}
private fun apply(path: String) {
var skinParentPath = path
if (path.contains("res")) {
skinParentPath = path.substringBeforeLast("res")
}
KeyboardThemeSaver.updateSkinPath(skinParentPath)
Toast.makeText(
this@KeyboardSettingActivity,
getString(R.string.theme_application_successful),
Toast.LENGTH_SHORT
).show()
startActivity(Intent(this, OperateSuccessActivity::class.java).apply {
putExtra(OperateSuccessActivity.key_name, name)
})
finish()
}
}

View File

@ -0,0 +1,131 @@
package com.app.intelligent.board.uiactivity;
import static com.bumptech.glide.request.RequestOptions.bitmapTransform;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.ad.tradpluslibrary.TPAdManager;
import com.app.intelligent.board.R;
import com.app.intelligent.board.databinding.ActivityOperateSuccessBinding;
import com.app.intelligent.board.utils.KeyboardKeyConst;
import com.app.intelligent.board.utils.AppCommonUtils;
import com.app.intelligent.board.utils.KeyboardThemeSaver;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import jp.wasabeef.glide.transformations.BlurTransformation;
public class OperateSuccessActivity extends AppCompatActivity {
private ActivityOperateSuccessBinding vb;
public static String key_name = "key_name";
private int mPreviousKeyboardHeight = -1;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
vb = ActivityOperateSuccessBinding.inflate(getLayoutInflater());
EdgeToEdge.enable(this);
setContentView(vb.getRoot());
TPAdManager.INSTANCE.showTPAD(this,()->null);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
onInit();
}
public void onInit() {
String stringExtra = getIntent().getStringExtra(key_name);
vb.title.setText(stringExtra);
String curPath = KeyboardThemeSaver.INSTANCE.getSkinPath();
vb.idBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
if (curPath == null) {
return;
}
String bgPath = curPath+"res/drawable-xxhdpi-v4/"+ KeyboardKeyConst.previewBg;
Drawable bgDraw = AppCommonUtils.INSTANCE.getBgDrawable(this, bgPath);
if (bgDraw != null) {
Glide.with(this)
.load(bgDraw)
.apply(bitmapTransform(new BlurTransformation(15, 3))) // 设置模糊半径和模糊采样
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
vb.main.setBackground(resource);
return false;
}
})
.preload();
}
keyboardheight();
vb.et.requestFocus();
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
}
private void keyboardheight() {
final View rootView = getWindow().getDecorView();
rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
rootView.getWindowVisibleDisplayFrame(r);
int screenHeight = rootView.getRootView().getHeight();
int keypadHeight = screenHeight - r.bottom;
if (keypadHeight != mPreviousKeyboardHeight) {
if (mPreviousKeyboardHeight < keypadHeight) {
mPreviousKeyboardHeight = keypadHeight;
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) vb.et.getLayoutParams();
params.bottomMargin = mPreviousKeyboardHeight;
vb.et.setLayoutParams(params);
}
}
}
});
}
}

View File

@ -0,0 +1,762 @@
package com.app.intelligent.board.uiactivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.ad.tradpluslibrary.TPAdManager;
import com.app.intelligent.board.AppContextHelper;
import com.app.intelligent.board.R;
import com.app.intelligent.board.databinding.ActivitySearchBinding;
import com.app.intelligent.board.databinding.AdapterHomeItemSubBinding;
import com.app.intelligent.board.databinding.LayoutSearchHistoryBinding;
import com.app.intelligent.board.databinding.LayoutSearchTrendingBinding;
import com.app.intelligent.board.databinding.ItemSearchHistoryBinding;
import com.app.intelligent.board.databinding.ItemSearchTagSingleBinding;
import com.app.intelligent.board.uimodel.KeyboardItemInfo;
import com.app.intelligent.board.uimodel.KeyboardItemDataWrapper;
import com.app.intelligent.board.utils.AppCommonUtils;
import com.app.intelligent.board.utils.KeyboardRvItemDecoration;
import com.bumptech.glide.Glide;
import java.util.HashSet;
import java.util.Set;
public class SearchActivity extends AppCompatActivity {
private ActivitySearchBinding vb;
private FrameLayout flContent;
// 历史记录核心配置
private LayoutSearchHistoryBinding historyBinding;
private List<String> historyList = new ArrayList<>();
private HistoryAdapter historyAdapter;
private static final String SP_NAME = "search_history_sp";
private static final String KEY_HISTORY_STR = "history_str";
private static final int MAX_HISTORY_COUNT = 6;
private static final String SEPARATOR = "|";
// Trending相关
private LayoutSearchTrendingBinding trendingBinding;
private List<KeyboardItemInfo> trendingList = new ArrayList<>();
private TrendingAdapter trendingAdapter;
// 联想词相关
private RecyclerView tagRv;
private TagAdapter tagAdapter;
private List<String> matchedTags = new ArrayList<>();
// 搜索结果相关
private RecyclerView resultRv;
private ResultAdapter resultAdapter;
private List<KeyboardItemInfo> resultList = new ArrayList<>();
// 无结果页面View
private View noResultView;
// 固定尺寸常量
private static final int KEYBOARD_ITEM_HEIGHT = 240; // dp
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
vb = ActivitySearchBinding.inflate(getLayoutInflater());
setContentView(vb.getRoot());
flContent = vb.flContent;
// 系统栏Insets适配
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
// 初始化无结果页面View
initNoResultView();
// 初始化View
initHistoryView();
initTrendingView();
initTagView();
initResultView();
// 初始化数据
initHistoryRecord();
initTrendingData();
// 初始显示
showInitialView();
// 初始化监听
initListeners();
// 激活搜索框+弹出键盘
vb.etSearch.requestFocus();
showSoftInputForce();
}
// ====================== 无结果页面初始化 ======================
private void initNoResultView() {
// 创建无结果页面布局
noResultView = LayoutInflater.from(this).inflate(R.layout.layout_no_search_result, null);
noResultView.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
));
// 可选设置无结果提示文字也可以直接在xml中写死
TextView tvTip = noResultView.findViewById(R.id.tv_no_result_tip);
tvTip.setText("No relevant content found");
}
// ====================== 历史记录相关 ======================
private void initHistoryView() {
historyBinding = LayoutSearchHistoryBinding.inflate(getLayoutInflater());
historyAdapter = new HistoryAdapter(historyList);
historyBinding.rvHistoryList.setLayoutManager(new LinearLayoutManager(this) {
@Override
public boolean canScrollVertically() {
return false;
}
});
historyBinding.rvHistoryList.setAdapter(historyAdapter);
historyBinding.rvHistoryList.setNestedScrollingEnabled(false);
historyBinding.tvClearAll.setOnClickListener(v -> {
historyList.clear();
saveHistoryToSP();
updateHistoryView();
});
}
private void initHistoryRecord() {
SharedPreferences sp = getSharedPreferences(SP_NAME, MODE_PRIVATE);
String historyStr = sp.getString(KEY_HISTORY_STR, "");
historyList.clear();
if (!historyStr.isEmpty()) {
String[] historyArray = historyStr.split("\\" + SEPARATOR);
for (String keyword : historyArray) {
if (!keyword.isEmpty()) {
historyList.add(keyword);
}
}
}
if (historyList.size() > MAX_HISTORY_COUNT) {
historyList = historyList.subList(0, MAX_HISTORY_COUNT);
saveHistoryToSP();
}
updateHistoryView();
}
private void updateHistoryView() {
if (historyList.isEmpty()) {
historyBinding.rvHistoryList.setVisibility(View.GONE);
historyBinding.tvNoHistory.setVisibility(View.VISIBLE);
historyBinding.tvClearAll.setVisibility(View.GONE);
} else {
historyBinding.rvHistoryList.setVisibility(View.VISIBLE);
historyBinding.tvNoHistory.setVisibility(View.GONE);
historyBinding.tvClearAll.setVisibility(View.VISIBLE);
historyAdapter.notifyDataSetChanged();
}
}
private void saveHistoryToSP() {
SharedPreferences sp = getSharedPreferences(SP_NAME, MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < historyList.size(); i++) {
sb.append(historyList.get(i));
if (i < historyList.size() - 1) {
sb.append(SEPARATOR);
}
}
editor.putString(KEY_HISTORY_STR, sb.toString()).apply();
}
private void addToHistory(String keyword) {
if (keyword == null || keyword.trim().isEmpty()) return;
String trimKeyword = keyword.trim();
historyList.remove(trimKeyword);
historyList.add(0, trimKeyword);
if (historyList.size() > MAX_HISTORY_COUNT) {
historyList.remove(historyList.size() - 1);
}
saveHistoryToSP();
updateHistoryView();
}
// ====================== Trending相关 ======================
private void initTrendingView() {
trendingBinding = LayoutSearchTrendingBinding.inflate(getLayoutInflater());
trendingAdapter = new TrendingAdapter(trendingList, this);
trendingBinding.rvTrending.setLayoutManager(new GridLayoutManager(this, 1) {
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
RecyclerView.LayoutParams params = super.generateDefaultLayoutParams();
int targetHeight = (int) (KEYBOARD_ITEM_HEIGHT * getResources().getDisplayMetrics().density);
params.height = targetHeight;
return params;
}
});
trendingBinding.rvTrending.setAdapter(trendingAdapter);
trendingBinding.rvTrending.setNestedScrollingEnabled(false);
KeyboardRvItemDecoration listDecoration = new KeyboardRvItemDecoration(15, 15, 0);
if (trendingBinding.rvTrending.getItemDecorationCount() == 0) {
trendingBinding.rvTrending.addItemDecoration(listDecoration);
}
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
trendingBinding.rvTrending.setLayoutParams(params);
}
private void initTrendingData() {
trendingList.clear();
if (AppContextHelper.trendingKeyboards == null) {
AppContextHelper.trendingKeyboards = new ArrayList<>();
}
if (AppContextHelper.trendingKeyboards.isEmpty()) {
List<KeyboardItemInfo> allKeyboards = new ArrayList<>();
Set<String> allKeyboardIdSet = new HashSet<>();
if (AppContextHelper.list != null && !AppContextHelper.list.isEmpty()) {
for (KeyboardItemDataWrapper wrapper : AppContextHelper.list) {
if (wrapper == null || wrapper.getKeyboardList() == null) continue;
for (KeyboardItemInfo keyboard : wrapper.getKeyboardList()) {
if (keyboard != null && keyboard.getTitleName() != null) {
String uniqueKey = keyboard.getTitleName();
if (!allKeyboardIdSet.contains(uniqueKey)) {
allKeyboardIdSet.add(uniqueKey);
allKeyboards.add(keyboard);
}
}
}
}
}
if (allKeyboards.isEmpty()) {
for (int i = 0; i < 8; i++) {
KeyboardItemInfo testInfo = new KeyboardItemInfo();
testInfo.setTitleName("Test Keyboard " + (i+1));
testInfo.setThumbUrl("https://example.com/keyboard_" + (i+1) + ".png");
allKeyboards.add(testInfo);
}
}
Random random = new Random();
Set<Integer> selectedIndexes = new HashSet<>();
int needCount = Math.min(8, allKeyboards.size());
while (selectedIndexes.size() < needCount) {
selectedIndexes.add(random.nextInt(allKeyboards.size()));
}
AppContextHelper.trendingKeyboards.clear();
for (int index : selectedIndexes) {
AppContextHelper.trendingKeyboards.add(allKeyboards.get(index));
}
}
trendingList.addAll(AppContextHelper.trendingKeyboards);
trendingAdapter.notifyDataSetChanged();
trendingBinding.rvTrending.setVisibility(View.VISIBLE);
}
// ====================== 联想词View初始化 ======================
private void initTagView() {
tagRv = new RecyclerView(this);
tagAdapter = new TagAdapter(matchedTags);
tagRv.setLayoutManager(new LinearLayoutManager(this));
tagRv.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT
));
tagRv.setBackgroundResource(R.drawable.shape_search_tag_container);
tagRv.setPadding(dp2px(12), dp2px(12), dp2px(12), dp2px(12));
tagRv.setClipToPadding(true);
tagRv.setAdapter(tagAdapter);
}
// ====================== 搜索结果View初始化 ======================
private void initResultView() {
resultRv = new RecyclerView(this);
resultAdapter = new ResultAdapter(resultList, this);
resultRv.setLayoutManager(new GridLayoutManager(this, 1) {
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
RecyclerView.LayoutParams params = super.generateDefaultLayoutParams();
int targetHeight = (int) (KEYBOARD_ITEM_HEIGHT * getResources().getDisplayMetrics().density);
params.height = targetHeight;
return params;
}
});
resultRv.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
));
KeyboardRvItemDecoration listDecoration = new KeyboardRvItemDecoration(15, 15, 0);
if (resultRv.getItemDecorationCount() == 0) {
resultRv.addItemDecoration(listDecoration);
}
resultRv.setAdapter(resultAdapter);
resultRv.setNestedScrollingEnabled(false);
}
// ====================== 视图切换逻辑 ======================
private void showInitialView() {
flContent.removeAllViews();
LinearLayout container = new LinearLayout(this);
container.setOrientation(LinearLayout.VERTICAL);
container.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
));
removeViewFromParent(historyBinding.getRoot());
removeViewFromParent(trendingBinding.getRoot());
container.addView(historyBinding.getRoot());
container.addView(trendingBinding.getRoot());
flContent.addView(container);
}
private void showTagView() {
flContent.removeAllViews();
flContent.addView(tagRv);
tagAdapter.notifyDataSetChanged();
}
private void showResultView() {
flContent.removeAllViews();
// 无结果显示无结果页面有结果显示列表
if (resultList.isEmpty()) {
flContent.addView(noResultView);
} else {
TPAdManager.INSTANCE.showTPAD(this,()-> {
flContent.addView(resultRv);
return null;
});
}
}
private void removeViewFromParent(View view) {
if (view.getParent() != null) {
((ViewGroup) view.getParent()).removeView(view);
}
}
// ====================== 数据加载逻辑 ======================
private void matchTags(String key) {
matchedTags.clear();
Set<String> titleSet = new HashSet<>();
if (AppContextHelper.list != null) {
for (KeyboardItemDataWrapper wrapper : AppContextHelper.list) {
if (wrapper == null || wrapper.getKeyboardList() == null) continue;
for (KeyboardItemInfo keyboard : wrapper.getKeyboardList()) {
if (keyboard != null && keyboard.getTitleName() != null
&& keyboard.getTitleName().toLowerCase().contains(key.toLowerCase())
&& !titleSet.contains(keyboard.getTitleName())) {
titleSet.add(keyboard.getTitleName());
matchedTags.add(keyboard.getTitleName());
}
}
}
}
}
private void searchKeyboards(String key) {
resultList.clear();
if (AppContextHelper.list == null) return;
Set<String> keyboardIdSet = new HashSet<>();
for (KeyboardItemDataWrapper wrapper : AppContextHelper.list) {
if (wrapper == null || wrapper.getKeyboardList() == null) continue;
for (KeyboardItemInfo keyboard : wrapper.getKeyboardList()) {
if (keyboard != null
&& keyboard.getTitleName() != null
&& keyboard.getTitleName().toLowerCase().contains(key.toLowerCase())) {
String uniqueKey = keyboard.getTitleName();
if (!keyboardIdSet.contains(uniqueKey)) {
keyboardIdSet.add(uniqueKey);
resultList.add(keyboard);
}
}
}
}
resultAdapter.notifyDataSetChanged();
}
// ====================== 监听事件 ======================
private void initListeners() {
// 返回按钮点击事件
vb.ivBack.setOnClickListener(v -> TPAdManager.INSTANCE.showTPAD(this,()->{
finish();
return null;
})
);
// 软键盘搜索按钮拦截
vb.etSearch.setOnEditorActionListener((v, actionId, event) -> {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
String key = vb.etSearch.getText().toString().trim();
if (!key.isEmpty()) {
addToHistory(key);
searchKeyboards(key);
showResultView();
hideSoftInput();
}
return true;
}
return false;
});
vb.etSearch.addTextChangedListener(new TextWatcher() {
private String lastKey = "";
private boolean isSwitching = false;
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (isSwitching) return;
String key = s.toString().trim();
if (key.equals(lastKey)) return;
lastKey = key;
matchedTags.clear();
if (!key.isEmpty()) {
matchTags(key);
isSwitching = true;
if (matchedTags.isEmpty()) {
searchKeyboards(key);
showResultView();
} else {
showTagView();
}
isSwitching = false;
} else {
vb.etSearch.postDelayed(() -> {
isSwitching = true;
showInitialView();
resultList.clear();
resultAdapter.notifyDataSetChanged();
isSwitching = false;
}, 100);
}
}
@Override
public void afterTextChanged(Editable s) {}
});
// 搜索按钮点击事件
vb.ivSearchBtn.setOnClickListener(v -> {
String key = vb.etSearch.getText().toString().trim();
if (key.isEmpty()) {
vb.etSearch.setError("Enter search keywords");
return;
}
addToHistory(key);
searchKeyboards(key);
showResultView();
hideSoftInput();
});
}
// ====================== 工具方法 ======================
private void showSoftInputForce() {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
private void hideSoftInput() {
try {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(vb.etSearch.getWindowToken(), 0);
} catch (Exception e) {
e.printStackTrace();
}
}
private int dp2px(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
}
// ====================== 适配器 ======================
private class HistoryAdapter extends RecyclerView.Adapter<HistoryAdapter.HistoryViewHolder> {
private List<String> dataList;
public HistoryAdapter(List<String> dataList) {
this.dataList = dataList;
}
@NonNull
@Override
public HistoryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemSearchHistoryBinding binding = ItemSearchHistoryBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false);
return new HistoryViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull HistoryViewHolder holder, int position) {
String keyword = dataList.get(position);
holder.binding.tvHistoryKeyword.setText(keyword);
holder.itemView.setOnClickListener(v -> {
addToHistory(keyword);
vb.etSearch.setText(keyword);
searchKeyboards(keyword);
showResultView();
hideSoftInput();
});
holder.binding.ivHistoryDelete.setOnClickListener(v -> {
dataList.remove(position);
saveHistoryToSP();
notifyItemRemoved(position);
notifyItemRangeChanged(position, dataList.size());
updateHistoryView();
});
}
@Override
public int getItemCount() {
return dataList.size();
}
class HistoryViewHolder extends RecyclerView.ViewHolder {
ItemSearchHistoryBinding binding;
public HistoryViewHolder(@NonNull ItemSearchHistoryBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
private class TagAdapter extends RecyclerView.Adapter<TagAdapter.TagViewHolder> {
private List<String> tagList;
public TagAdapter(List<String> tagList) {
this.tagList = tagList;
}
@NonNull
@Override
public TagViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemSearchTagSingleBinding binding = ItemSearchTagSingleBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false);
return new TagViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull TagViewHolder holder, int position) {
String tag = tagList.get(position);
holder.binding.tvTag.setText(tag);
holder.itemView.setOnClickListener(v -> {
vb.etSearch.setText(tag);
addToHistory(tag);
searchKeyboards(tag);
showResultView();
hideSoftInput();
});
}
@Override
public int getItemCount() {
return tagList.size();
}
class TagViewHolder extends RecyclerView.ViewHolder {
ItemSearchTagSingleBinding binding;
public TagViewHolder(@NonNull ItemSearchTagSingleBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
private class TrendingAdapter extends RecyclerView.Adapter<TrendingAdapter.TrendingViewHolder> {
private List<KeyboardItemInfo> mList;
private Context mContext;
public TrendingAdapter(List<KeyboardItemInfo> mList, Context mContext) {
this.mList = mList;
this.mContext = mContext;
}
@NonNull
@Override
public TrendingViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
AdapterHomeItemSubBinding inflate = AdapterHomeItemSubBinding.inflate(
LayoutInflater.from(parent.getContext()),
parent,
false
);
return new TrendingViewHolder(inflate);
}
@Override
public void onBindViewHolder(@NonNull TrendingViewHolder holder, int position) {
if (position >= mList.size()) return;
KeyboardItemInfo beanDetails = mList.get(position);
String thumbGif = beanDetails.getThumbGif() == null ? "" : beanDetails.getThumbGif();
String thumb = beanDetails.getThumbUrl() == null ? "" : beanDetails.getThumbUrl();
if (!thumbGif.isEmpty()) {
AppCommonUtils.INSTANCE.loadWepJif(mContext, thumbGif, holder.binding.imageView);
} else if (!thumb.isEmpty()) {
Glide.with(mContext).load(thumb)
.error(R.drawable.place_holder)
.placeholder(R.drawable.place_holder)
.into(holder.binding.imageView);
} else {
holder.binding.imageView.setImageResource(R.drawable.place_holder);
}
holder.binding.fragme.setOnClickListener(v -> {
if (beanDetails == null) return;
Intent intentApply = new Intent(mContext, KeyboardSettingActivity.class);
intentApply.putExtra(KeyboardSettingActivity.SOURCE_KEY, beanDetails);
intentApply.putExtra(KeyboardSettingActivity.DISPLAY_URL_KEY, beanDetails.getImgPath());
intentApply.putExtra(KeyboardSettingActivity.ZIP_URL_KEY, beanDetails.getZipPath());
intentApply.putExtra(KeyboardSettingActivity.NAME_KEY, beanDetails.getTitleName());
intentApply.putExtra(KeyboardSettingActivity.GIF_KEY, beanDetails.getImgGif());
String intent_thumb = !thumbGif.isEmpty() ? thumbGif : thumb;
intentApply.putExtra(KeyboardSettingActivity.THUMB_KEY, intent_thumb);
mContext.startActivity(intentApply);
});
}
@Override
public int getItemCount() {
return mList.size();
}
class TrendingViewHolder extends RecyclerView.ViewHolder {
AdapterHomeItemSubBinding binding;
public TrendingViewHolder(@NonNull AdapterHomeItemSubBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
private class ResultAdapter extends RecyclerView.Adapter<ResultAdapter.ResultViewHolder> {
private List<KeyboardItemInfo> mList;
private Context mContext;
public ResultAdapter(List<KeyboardItemInfo> mList, Context mContext) {
this.mList = mList;
this.mContext = mContext;
}
@NonNull
@Override
public ResultViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
AdapterHomeItemSubBinding inflate = AdapterHomeItemSubBinding.inflate(
LayoutInflater.from(parent.getContext()),
parent,
false
);
return new ResultViewHolder(inflate);
}
@Override
public void onBindViewHolder(@NonNull ResultViewHolder holder, int position) {
if (position >= mList.size()) return;
KeyboardItemInfo beanDetails = mList.get(position);
String thumbGif = beanDetails.getThumbGif() == null ? "" : beanDetails.getThumbGif();
String thumb = beanDetails.getThumbUrl() == null ? "" : beanDetails.getThumbUrl();
if (!thumbGif.isEmpty()) {
AppCommonUtils.INSTANCE.loadWepJif(mContext, thumbGif, holder.binding.imageView);
} else if (!thumb.isEmpty()) {
Glide.with(mContext).load(thumb)
.error(R.drawable.place_holder)
.placeholder(R.drawable.place_holder)
.into(holder.binding.imageView);
} else {
holder.binding.imageView.setImageResource(R.drawable.place_holder);
}
holder.binding.fragme.setOnClickListener(v -> {
if (beanDetails == null) return;
Intent intentApply = new Intent(mContext, KeyboardSettingActivity.class);
intentApply.putExtra(KeyboardSettingActivity.SOURCE_KEY, beanDetails);
intentApply.putExtra(KeyboardSettingActivity.DISPLAY_URL_KEY, beanDetails.getImgPath());
intentApply.putExtra(KeyboardSettingActivity.ZIP_URL_KEY, beanDetails.getZipPath());
intentApply.putExtra(KeyboardSettingActivity.NAME_KEY, beanDetails.getTitleName());
intentApply.putExtra(KeyboardSettingActivity.GIF_KEY, beanDetails.getImgGif());
String intent_thumb = !thumbGif.isEmpty() ? thumbGif : thumb;
intentApply.putExtra(KeyboardSettingActivity.THUMB_KEY, intent_thumb);
mContext.startActivity(intentApply);
});
}
@Override
public int getItemCount() {
return mList.size();
}
class ResultViewHolder extends RecyclerView.ViewHolder {
AdapterHomeItemSubBinding binding;
public ResultViewHolder(@NonNull AdapterHomeItemSubBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
}

View File

@ -0,0 +1,68 @@
package com.app.intelligent.board.uiactivity
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
import android.widget.ProgressBar
import com.ad.tradpluslibrary.TPAdManager
import com.app.intelligent.board.AppContextHelper
import com.app.intelligent.board.R
import com.app.intelligent.board.utils.AppCommonUtils
import kotlin.math.roundToInt
/**
* 不要修改启动页继承Activity这点
*/
class StartActivity : Activity() {
private lateinit var progressBar: ProgressBar
private var countTime = 15000L
private lateinit var timer: CountDownTimer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_start)
AppCommonUtils.initFullScreen(this,true)
progressBar = findViewById<ProgressBar>(R.id.nova_progress)
init()
}
private fun init() {
TPAdManager.init(
this,
AppContextHelper.TAG,
"3B146C8C20991851D63D84716B4F7111",
"C1C535F4709BE310CB0D64F2408B1912",
"EA57BAD08FDF4426503AF8AD94F53F12",
"F79B74423BCD821135025ACA55FD2E12"
) {
}
timer = TPAdManager.showWelcomeAd(this, countTime, { millisUntilFinished ->
//倒计时更新
val progressPercentage = (100 * millisUntilFinished) / countTime
val percentage = 100 - progressPercentage
progressBar.progress = percentage.toInt()
}) {
progressBar.progress = 100
toHome()
}
timer.start()
}
private fun toHome() {
startActivity(Intent(this, CategoryMainActivity::class.java))
finish()
}
override fun onDestroy() {
super.onDestroy()
timer.cancel()
}
}

View File

@ -0,0 +1,124 @@
package com.app.intelligent.board.uiadapter;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import com.app.intelligent.board.uimodel.KeyboardItemInfo;
import com.bumptech.glide.Glide;
import com.app.intelligent.board.R;
import com.app.intelligent.board.utils.AppCommonUtils;
import com.app.intelligent.board.uilistener.FavoriteItemDeleteListener;
import com.app.intelligent.board.uiactivity.KeyboardSettingActivity;
import java.util.ArrayList;
import java.util.List;
public class FavoriteItemAdapter extends RecyclerView.Adapter<FavoriteItemAdapter.ForYouViewHolder> {
private Context mContext;
private List<KeyboardItemInfo> mList = new ArrayList<>();
private FavoriteItemDeleteListener mCallBack;
public FavoriteItemAdapter(Context context) {
mContext = context;
}
public void setForYouList(List<KeyboardItemInfo> list) {
this.mList = (list == null) ? new ArrayList<>() : list;
notifyDataSetChanged();
}
public void setRemoveLike(FavoriteItemDeleteListener callback) {
mCallBack = callback;
}
@NonNull
@Override
public ForYouViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.adapter_favorite_item, parent, false);
return new ForYouViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ForYouViewHolder holder, int position) {
KeyboardItemInfo beanDetails = mList.get(position);
String thumbGif = beanDetails.getThumbGif();
String thumb = beanDetails.getThumbUrl();
// 加载图片
if (thumbGif != null && !thumbGif.isEmpty()) {
AppCommonUtils.INSTANCE.loadWepJif(mContext, thumbGif, holder.itemImg);
} else {
Glide.with(mContext)
.load(thumb)
.error(R.drawable.place_holder)
.placeholder(R.drawable.place_holder)
.into(holder.itemImg);
}
// 收藏按钮仅设置选中状态删除原点击监听
holder.itemFavorite.setSelected(true);
// 清除按钮逻辑仅保留清除按钮的删除逻辑
holder.layoutClear.setOnClickListener(v -> {
handleRemoveLike(holder, beanDetails);
});
// 条目点击跳转
holder.cardView.setOnClickListener(v -> {
Intent intentApply = new Intent(mContext, KeyboardSettingActivity.class);
intentApply.putExtra(KeyboardSettingActivity.SOURCE_KEY, beanDetails);
intentApply.putExtra(KeyboardSettingActivity.DISPLAY_URL_KEY, beanDetails.getImgPath());
intentApply.putExtra(KeyboardSettingActivity.ZIP_URL_KEY, beanDetails.getZipPath());
intentApply.putExtra(KeyboardSettingActivity.NAME_KEY, beanDetails.getTitleName());
intentApply.putExtra(KeyboardSettingActivity.GIF_KEY, beanDetails.getImgGif());
String intent_thumb = (thumbGif != null && !thumbGif.isEmpty()) ? thumbGif : thumb;
intentApply.putExtra(KeyboardSettingActivity.THUMB_KEY, intent_thumb);
mContext.startActivity(intentApply);
});
}
// 封装删除逻辑
private void handleRemoveLike(ForYouViewHolder holder, KeyboardItemInfo beanDetails) {
holder.itemFavorite.setSelected(false);
int adapterPosition = holder.getAdapterPosition();
if (adapterPosition != RecyclerView.NO_POSITION) {
mList.remove(adapterPosition);
notifyItemRemoved(adapterPosition);
notifyItemRangeChanged(adapterPosition, mList.size());
if (mCallBack != null) {
mCallBack.OnRemoveLike(beanDetails);
}
}
}
@Override
public int getItemCount() {
return mList.size();
}
public static class ForYouViewHolder extends RecyclerView.ViewHolder {
CardView cardView;
FrameLayout layoutFavorite, layoutClear;
ImageView itemImg, itemFavorite, imClear;
public ForYouViewHolder(@NonNull View itemView) {
super(itemView);
cardView = itemView.findViewById(R.id.card_view);
layoutFavorite = itemView.findViewById(R.id.layout_favorite);
layoutClear = itemView.findViewById(R.id.layout_clear);
itemImg = itemView.findViewById(R.id.im);
itemFavorite = itemView.findViewById(R.id.im_favorite);
imClear = itemView.findViewById(R.id.im_clear);
}
}
}

View File

@ -0,0 +1,110 @@
package com.app.intelligent.board.uiadapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.app.intelligent.board.uimodel.KeyboardItemDataWrapper;
import com.app.intelligent.board.uimodel.KeyboardItemInfo;
import com.app.intelligent.board.uilistener.KeyboardViewExpandListener;
import com.app.intelligent.board.databinding.AdapterHomeItemBinding;
import com.app.intelligent.board.utils.KeyboardRvItemDecoration;
import java.util.ArrayList;
import java.util.List;
public class HomeItemAdapter extends RecyclerView.Adapter<HomeItemAdapter.MainViewHolder> {
private Context mContext;
private List<KeyboardItemDataWrapper> mList = new ArrayList<>();
private KeyboardViewExpandListener mCallBack;
public HomeItemAdapter(Context context, List<KeyboardItemDataWrapper> list) {
mContext = context;
this.mList = list;
}
public void setClickAction(KeyboardViewExpandListener callback) {
mCallBack = callback;
}
@NonNull
@Override
public MainViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
AdapterHomeItemBinding inflate = AdapterHomeItemBinding.inflate(
LayoutInflater.from(parent.getContext()),
parent,
false
);
return new MainViewHolder(inflate);
}
@Override
public void onBindViewHolder(@NonNull MainViewHolder holder, int position) {
KeyboardItemDataWrapper beanWrapper = mList.get(position);
String parentName = beanWrapper.getParentName();
holder.binding.className.setText(parentName);
KeyboardRvItemDecoration listDecoration = new KeyboardRvItemDecoration(8, 8, 0);
// 核心修复将List<?>转为HomeChildAdapter需要的List<BeanDetails>
List<KeyboardItemInfo> keyboardList = new ArrayList<>();
if (beanWrapper.getKeyboardList() != null) {
// 强转成目标类型匹配HomeChildAdapter的构造函数
for (Object item : beanWrapper.getKeyboardList()) {
if (item instanceof KeyboardItemInfo) {
keyboardList.add((KeyboardItemInfo) item);
}
}
}
// 传入正确类型的List<BeanDetails>
HomeItemSubAdapter homeChildAdapter = new HomeItemSubAdapter(mContext, keyboardList);
// 核心修改自定义GridLayoutManager强制子项高度为80dp可调整
holder.binding.childRecycler.setLayoutManager(new GridLayoutManager(mContext, 1) {
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
RecyclerView.LayoutParams params = super.generateDefaultLayoutParams();
// 将80dp转为像素值关键和adapter_home_child.xml的高度一致
int targetHeight = (int) (240 * mContext.getResources().getDisplayMetrics().density);
params.height = targetHeight;
return params;
}
});
holder.binding.childRecycler.setAdapter(homeChildAdapter);
if (holder.binding.childRecycler.getItemDecorationCount() == 0) {
holder.binding.childRecycler.addItemDecoration(listDecoration);
}
holder.binding.tvAll.setOnClickListener(v -> {
if (mCallBack != null) {
mCallBack.OnClickSeeAll(parentName);
}
});
}
@Override
public int getItemCount() {
return mList.size();
}
public void refreshData(List<KeyboardItemDataWrapper> newList) {
this.mList.clear();
this.mList.addAll(newList);
notifyDataSetChanged();
}
public static class MainViewHolder extends RecyclerView.ViewHolder {
private AdapterHomeItemBinding binding;
public MainViewHolder(@NonNull AdapterHomeItemBinding itemView) {
super(itemView.getRoot());
binding = itemView;
}
}
}

View File

@ -0,0 +1,84 @@
package com.app.intelligent.board.uiadapter;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.app.intelligent.board.R;
import com.app.intelligent.board.uimodel.KeyboardItemInfo;
import com.app.intelligent.board.databinding.AdapterHomeItemSubBinding;
import com.app.intelligent.board.uiactivity.KeyboardSettingActivity;
import com.app.intelligent.board.utils.AppCommonUtils;
import com.bumptech.glide.Glide;
import java.util.ArrayList;
import java.util.List;
public class HomeItemSubAdapter extends RecyclerView.Adapter<HomeItemSubAdapter.ChildViewHolder> {
private Context mContext;
private List<KeyboardItemInfo> mList = new ArrayList<>();
public HomeItemSubAdapter(Context context, List<KeyboardItemInfo> list) {
mContext = context;
this.mList = list;
}
@NonNull
@Override
public ChildViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 核心修复添加 parent attachToRoot=false让布局受父容器约束
AdapterHomeItemSubBinding inflate = AdapterHomeItemSubBinding.inflate(
LayoutInflater.from(parent.getContext()),
parent,
false // 关键禁止直接附加到父容器由RecyclerView管理
);
return new ChildViewHolder(inflate);
}
@Override
public void onBindViewHolder(@NonNull ChildViewHolder holder, int position) {
KeyboardItemInfo beanDetails = mList.get(position);
String thumbGif = beanDetails.getThumbGif();
String thumb = beanDetails.getThumbUrl();
if (!thumbGif.isEmpty()) {
AppCommonUtils.INSTANCE.loadWepJif(mContext, thumbGif, holder.binding.imageView);
} else {
Glide.with(mContext).load(thumb)
.error(R.drawable.place_holder)
.placeholder(R.drawable.place_holder)
.into(holder.binding.imageView);
}
holder.binding.fragme.setOnClickListener(v -> {
Intent intentApply = new Intent(mContext, KeyboardSettingActivity.class);
intentApply.putExtra(KeyboardSettingActivity.SOURCE_KEY, beanDetails);
intentApply.putExtra(KeyboardSettingActivity.DISPLAY_URL_KEY, beanDetails.getImgPath());
intentApply.putExtra(KeyboardSettingActivity.ZIP_URL_KEY, beanDetails.getZipPath());
intentApply.putExtra(KeyboardSettingActivity.NAME_KEY, beanDetails.getTitleName());
intentApply.putExtra(KeyboardSettingActivity.GIF_KEY, beanDetails.getImgGif());
String intent_thumb = !thumbGif.isEmpty() ? thumbGif : thumb;
intentApply.putExtra(KeyboardSettingActivity.THUMB_KEY, intent_thumb);
mContext.startActivity(intentApply);
});
}
@Override
public int getItemCount() {
return mList.size();
}
public static class ChildViewHolder extends RecyclerView.ViewHolder {
private AdapterHomeItemSubBinding binding;
public ChildViewHolder(@NonNull AdapterHomeItemSubBinding itemView) {
super(itemView.getRoot());
binding = itemView;
}
}
}

View File

@ -0,0 +1,50 @@
package com.app.intelligent.board.uiadapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.app.intelligent.board.R
import com.app.intelligent.board.uimodel.KeyboardItemInfo
import com.app.intelligent.board.uilistener.KeyboardItemClickListener
class KeyboardItemAdapter(
private val context: Context,
private val allItems: List<KeyboardItemInfo>,
private val columnCount: Int = 2,
private val itemClickCallback: KeyboardItemClickListener
) : RecyclerView.Adapter<KeyboardItemAdapter.PageViewHolder>() {
// 每页显示 2行*2列=4个item
private val itemsPerPage = columnCount * 2
private val pageCount = (allItems.size + itemsPerPage - 1) / itemsPerPage
inner class PageViewHolder(itemView: android.view.View) : RecyclerView.ViewHolder(itemView) {
val pageRecyclerView: RecyclerView = itemView.findViewById(R.id.page_recycler)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PageViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.item_keyboard_tab, parent, false)
return PageViewHolder(view)
}
override fun onBindViewHolder(holder: PageViewHolder, position: Int) {
// 计算当前页的item数据区间
val startIndex = position * itemsPerPage
val endIndex = kotlin.math.min(startIndex + itemsPerPage, allItems.size)
val pageItems = allItems.subList(startIndex, endIndex)
// 配置当前页的RecyclerView
holder.pageRecyclerView.apply {
layoutManager = GridLayoutManager(context, columnCount)
adapter = KeyboardOptionAdapter(context).apply {
setForYouList(pageItems)
setClickAction(itemClickCallback)
}
}
}
override fun getItemCount(): Int = pageCount
}

View File

@ -0,0 +1,127 @@
package com.app.intelligent.board.uiadapter;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import com.app.intelligent.board.R;
import com.app.intelligent.board.uimodel.KeyboardItemInfo;
import com.app.intelligent.board.uilistener.KeyboardItemClickListener;
import com.app.intelligent.board.uiactivity.KeyboardSettingActivity;
import com.bumptech.glide.Glide;
import com.app.intelligent.board.utils.AppCommonUtils;
import java.util.ArrayList;
import java.util.List;
public class KeyboardOptionAdapter extends RecyclerView.Adapter<KeyboardOptionAdapter.ForYouViewHolder> {
private Context mContext;
private List<KeyboardItemInfo> mList = new ArrayList<>();
private KeyboardItemClickListener mCallBack;
public KeyboardOptionAdapter(Context context) {
mContext = context;
}
public void setForYouList(List<KeyboardItemInfo> list) {
this.mList = list;
notifyDataSetChanged();
}
public void setClickAction(KeyboardItemClickListener callback) {
mCallBack = callback;
}
@NonNull
@Override
public ForYouViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.adapter_keyboard_option, parent, false);
return new ForYouViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ForYouViewHolder holder, int position) {
KeyboardItemInfo beanDetails = mList.get(position);
String thumbGif = beanDetails.getThumbGif();
String thumb = beanDetails.getThumbUrl();
if (!thumbGif.isEmpty()) {
AppCommonUtils.INSTANCE.loadWepJif(mContext, thumbGif, holder.itemImg);
} else {
Glide.with(mContext).load(thumb).into(holder.itemImg);
}
holder.cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intentApply = new Intent(mContext, KeyboardSettingActivity.class);
intentApply.putExtra(KeyboardSettingActivity.SOURCE_KEY, beanDetails);
intentApply.putExtra(KeyboardSettingActivity.DISPLAY_URL_KEY, beanDetails.getImgPath());
intentApply.putExtra(KeyboardSettingActivity.ZIP_URL_KEY, beanDetails.getZipPath());
intentApply.putExtra(KeyboardSettingActivity.NAME_KEY, beanDetails.getTitleName());
intentApply.putExtra(KeyboardSettingActivity.GIF_KEY, beanDetails.getImgGif());
String intent_thumb;
if (!thumbGif.isEmpty()) {
intent_thumb = thumbGif;
} else {
intent_thumb = thumb;
}
intentApply.putExtra(KeyboardSettingActivity.THUMB_KEY, intent_thumb);
mContext.startActivity(intentApply);
if (mCallBack != null) {
mCallBack.OnItemClickListener();
}
}
});
}
@Override
public int getItemCount() {
return mList.size();
}
public static class ForYouViewHolder extends RecyclerView.ViewHolder {
private CardView cardView;
private ImageView itemImg;
public ForYouViewHolder(@NonNull View itemView) {
super(itemView);
cardView = itemView.findViewById(R.id.card_view);
itemImg = itemView.findViewById(R.id.imPreview);
}
}
// private void loadWepJif(String webpGifUrl,ImageView view){
// Glide.with(mContext).load(webpGifUrl).addListener(new RequestListener<Drawable>() {
// @Override
// public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
// return false;
// }
//
// @Override
// public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
// if(resource instanceof WebpDrawable){
// WebpDrawable webpDrawable = (WebpDrawable) resource;
// webpDrawable.setLoopCount(LOOP_FOREVER);
// }
//
// return false;
// }
// }).into(view);
// }
}

View File

@ -0,0 +1,31 @@
package com.app.intelligent.board.uidb
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import com.app.intelligent.board.uimodel.KeyboardItemInfo
@Dao
interface ListItemDao {
@Insert(onConflict = OnConflictStrategy.Companion.IGNORE)
suspend fun insertData(data: KeyboardItemInfo): Long
@Query("select * from KeyboardItemInfo ")
fun queryAllLike(): LiveData<List<KeyboardItemInfo?>?>
@Query("select * from KeyboardItemInfo where titleName = :title ")
suspend fun queryIsLike(title: String ): KeyboardItemInfo?
@Delete
suspend fun delete(data: KeyboardItemInfo)
@Update
suspend fun updateLike(data: KeyboardItemInfo)
}

View File

@ -0,0 +1,30 @@
package com.app.intelligent.board.uidb
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.app.intelligent.board.AppContextHelper
import com.app.intelligent.board.uimodel.KeyboardItemInfo
@Database(
entities = [KeyboardItemInfo::class],
version = AppContextHelper.Companion.DB_VERSION,
exportSchema = false
)
abstract class MyAppDatabase : RoomDatabase() {
abstract fun ThemesDao(): ListItemDao
companion object {
val baseDataBase: MyAppDatabase by lazy {
Room.databaseBuilder(
AppContextHelper.Companion.appInstance, MyAppDatabase::class.java,
AppContextHelper.Companion.DB_NAME
).build()
}
}
}

View File

@ -0,0 +1,34 @@
package com.app.intelligent.board.uidb
import com.app.intelligent.board.uimodel.KeyboardItemInfo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
object MyAppDatabaseManager {
suspend fun addLike(data: KeyboardItemInfo) {
withContext(Dispatchers.IO) {
MyAppDatabase.baseDataBase.ThemesDao().insertData(data)
}
}
suspend fun removeLike(data: KeyboardItemInfo) {
withContext(Dispatchers.IO) {
val queryIsLike = MyAppDatabase.baseDataBase.ThemesDao().queryIsLike(data.titleName)
if (queryIsLike != null) {
MyAppDatabase.baseDataBase.ThemesDao().delete(queryIsLike)
}
}
}
suspend fun getIsLike(name: String, action: (isLike: Boolean) -> Unit) {
withContext(Dispatchers.IO) {
val query = MyAppDatabase.baseDataBase.ThemesDao().queryIsLike(name)
withContext(Dispatchers.Main) {
action.invoke(query != null)
}
}
}
}

View File

@ -0,0 +1,114 @@
package com.app.intelligent.board.uifragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.graphics.Rect
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.app.intelligent.board.AppContextHelper
import com.app.intelligent.board.uimodel.KeyboardItemInfo
import com.app.intelligent.board.databinding.FragmentCategoryItemContentBinding
import com.app.intelligent.board.uiadapter.HomeItemSubAdapter
// 确保项目中只有这一个CategoryContentFragment类
class CategoryItemContentFragment : Fragment() {
private lateinit var binding: FragmentCategoryItemContentBinding
private lateinit var categoryName: String
private lateinit var keyboardAdapter: HomeItemSubAdapter
private val allKeyboards = mutableListOf<KeyboardItemInfo>()
companion object {
private const val KEY_CATEGORY_NAME = "category_name"
fun newInstance(categoryName: String): CategoryItemContentFragment {
val fragment = CategoryItemContentFragment()
val args = Bundle().apply {
putString(KEY_CATEGORY_NAME, categoryName)
}
fragment.arguments = args
return fragment
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
categoryName = arguments?.getString(KEY_CATEGORY_NAME).orEmpty()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentCategoryItemContentBinding.inflate(inflater, container, false)
initRecyclerView()
loadAllKeyboards()
return binding.root
}
private fun initRecyclerView() {
keyboardAdapter = HomeItemSubAdapter(requireContext(), allKeyboards)
binding.categoryRecycler.apply {
adapter = keyboardAdapter
// 保留1列网格布局和原逻辑一致
layoutManager = GridLayoutManager(requireContext(), 1)
// 移除原有ListDecoration替换为自定义间距
if (itemDecorationCount > 0) {
removeItemDecorationAt(0)
}
// 核心添加自定义ItemDecoration强制设置上下间距
addItemDecoration(CustomKeyboardDecoration())
setHasFixedSize(true)
isNestedScrollingEnabled = true
}
}
// 自定义ItemDecoration控制键盘之间的上下间距
inner class CustomKeyboardDecoration : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
// 上下间距设为4dp
val verticalMargin = dp2px(4)
// 左右间距保留6dp
val horizontalMargin = dp2px(6)
outRect.top = verticalMargin
outRect.bottom = verticalMargin
outRect.left = horizontalMargin
outRect.right = horizontalMargin
}
}
// 工具方法dp转px适配不同屏幕
private fun dp2px(dp: Int): Int {
return (dp * resources.displayMetrics.density).toInt()
}
private fun loadAllKeyboards() {
allKeyboards.clear()
AppContextHelper.list.forEach { beanWrapper ->
if (beanWrapper.parentName == categoryName) {
@Suppress("UNCHECKED_CAST")
val keyboardList = beanWrapper.getKeyboardList() as? List<KeyboardItemInfo>
keyboardList?.let { allKeyboards.addAll(it) }
}
}
keyboardAdapter.notifyDataSetChanged()
println("分类[$categoryName]加载键盘数:${allKeyboards.size}")
}
fun refreshCategoryData(newCategoryName: String) {
if (newCategoryName.isNotEmpty()) {
categoryName = newCategoryName
loadAllKeyboards()
}
}
}

View File

@ -0,0 +1,73 @@
package com.app.intelligent.board.uifragment
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager // 改用LinearLayoutManager
import com.app.intelligent.board.AppContextHelper
import com.app.intelligent.board.uimodel.KeyboardItemInfo
import com.app.intelligent.board.databinding.FragmentFavoritesKeyboardBinding
import com.app.intelligent.board.uidb.MyAppDatabase
import com.app.intelligent.board.uidb.MyAppDatabaseManager
import com.app.intelligent.board.uilistener.FavoriteItemDeleteListener
import com.app.intelligent.board.uiadapter.FavoriteItemAdapter
import com.app.intelligent.board.utils.KeyboardRvItemDecoration
import kotlinx.coroutines.launch
class FavoritesKeyboardFragment : Fragment() {
private lateinit var vb: FragmentFavoritesKeyboardBinding
companion object {
@JvmStatic
fun newInstance() = FavoritesKeyboardFragment()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
vb = FragmentFavoritesKeyboardBinding.inflate(layoutInflater)
init()
return vb.root
}
private fun init() {
val mainAdapter = FavoriteItemAdapter(
requireContext()
).apply {
setRemoveLike(object : FavoriteItemDeleteListener {
override fun OnRemoveLike(data: KeyboardItemInfo) {
lifecycleScope.launch {
MyAppDatabaseManager.removeLike(data)
}
}
})
}
// 配置RecyclerView改用LinearLayoutManager一行一列更适配
vb.likeRecycler.run {
adapter = mainAdapter
layoutManager = LinearLayoutManager(requireContext()) // 核心:替换为线性布局
// 原15改为5缩小RecyclerView的条目间距
addItemDecoration(KeyboardRvItemDecoration(5, 5, 0))
setHasFixedSize(true) // 提升性能
}
// 监听数据变化
MyAppDatabase.Companion.baseDataBase.ThemesDao().queryAllLike().observe(requireActivity()) {
Log.d(AppContextHelper.Companion.TAG, "---------it=${it?.size}")
if(it.isNullOrEmpty()){
vb.likeRecycler.isVisible = false
vb.emptyTitle.isVisible = true
}else{
vb.likeRecycler.isVisible = true
vb.emptyTitle.isVisible = false
mainAdapter.setForYouList(it)
}
}
}
}

View File

@ -0,0 +1,162 @@
package com.app.intelligent.board.uifragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.graphics.PorterDuff
import android.widget.ArrayAdapter
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentPagerAdapter
import com.app.intelligent.board.AppContextHelper
import com.app.intelligent.board.R
import com.app.intelligent.board.databinding.FragmentHomeMainBinding
import android.widget.ImageView
import android.widget.TextView
import android.graphics.Color
import android.content.Intent // 新增导入Intent
import com.app.intelligent.board.uiactivity.SearchActivity // 新增导入搜索页面Activity
class HomeMainFragment : Fragment() {
private lateinit var vb: FragmentHomeMainBinding
private val tabTitles by lazy {
AppContextHelper.list.mapNotNull { it.parentName }.distinct()
}
private var currentSelectedPos = 0 // 记录当前选中位置
companion object {
@JvmStatic
fun newInstance() = HomeMainFragment()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
vb = FragmentHomeMainBinding.inflate(layoutInflater, container, false)
initSpinnerAndViewPager()
initSearchButton() // 初始化搜索按钮
return vb.root
}
/**
* 初始化搜索按钮点击事件实现跳转
*/
private fun initSearchButton() {
vb.ivSearch.setOnClickListener {
// 1. 创建Intent跳转到SearchActivity
val intent = Intent(requireContext(), SearchActivity::class.java)
// 2. 可选:传递当前选中的分类(供搜索页面使用)
intent.putExtra("selectedCategory", tabTitles[currentSelectedPos])
// 3. 启动搜索页面
startActivity(intent)
}
}
// 以下代码保持不变...
private fun initSpinnerAndViewPager() {
// 1. 初始化ViewPager适配器
val pagerAdapter = object : FragmentPagerAdapter(childFragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getCount(): Int = tabTitles.size
override fun getItem(position: Int): Fragment = CategoryItemContentFragment.newInstance(tabTitles[position])
override fun getPageTitle(position: Int): CharSequence? = tabTitles[position]
}
// 2. 绑定ViewPager
vb.viewPager.adapter = pagerAdapter
vb.viewPager.offscreenPageLimit = if (tabTitles.size > 3) 3 else tabTitles.size - 1
// 3. 初始化Spinner下拉选择器
initSpinner()
}
private fun initSpinner() {
val spinnerAdapter = object : ArrayAdapter<String>(
requireContext(),
R.layout.custom_spinner_item,
tabTitles
) {
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: LayoutInflater.from(context).inflate(R.layout.custom_spinner_dropdown_item, parent, false)
val textView = view.findViewById<TextView>(R.id.spinner_text)
textView.text = tabTitles[position]
if (position == currentSelectedPos) {
textView.setBackgroundResource(R.drawable.spinner_selected_bg)
textView.setTextColor(Color.DKGRAY)
} else {
textView.setBackgroundResource(0)
textView.setTextColor(resources.getColor(R.color.main_text_color))
}
return view
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: LayoutInflater.from(context).inflate(R.layout.custom_spinner_item, parent, false)
val textView = view.findViewById<TextView>(R.id.spinner_text)
val arrowView = view.findViewById<ImageView>(R.id.spinner_arrow)
textView.text = tabTitles[position]
val targetColor = resources.getColor(R.color.dark_gray)
textView.setTextColor(targetColor)
arrowView.setColorFilter(targetColor, PorterDuff.Mode.SRC_IN)
return view
}
}
vb.categorySpinner.adapter = spinnerAdapter
// 宽度计算(适配文字+箭头+间距)
vb.categorySpinner.post {
val longestText = tabTitles.maxByOrNull { it.length } ?: "recommend"
val tempTextView = TextView(requireContext()).apply {
text = longestText
textSize = 16f
}
tempTextView.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
val arrowWidth = (14 * resources.displayMetrics.density).toInt()
val textArrowMargin = (30 * resources.displayMetrics.density).toInt()
val paddingWidth = (32 * resources.displayMetrics.density).toInt()
val finalWidth = tempTextView.measuredWidth + arrowWidth + textArrowMargin + paddingWidth
vb.categorySpinner.layoutParams.width = finalWidth
vb.categorySpinner.requestLayout()
}
// Spinner选择监听
vb.categorySpinner.onItemSelectedListener = object : android.widget.AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: android.widget.AdapterView<*>, view: View?, position: Int, id: Long) {
currentSelectedPos = position
vb.viewPager.currentItem = position
spinnerAdapter.notifyDataSetChanged()
}
override fun onNothingSelected(parent: android.widget.AdapterView<*>) {}
}
// ViewPager切换监听
vb.viewPager.addOnPageChangeListener(object : androidx.viewpager.widget.ViewPager.OnPageChangeListener {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {
currentSelectedPos = position
vb.categorySpinner.setSelection(position, false)
spinnerAdapter.notifyDataSetChanged()
}
override fun onPageScrollStateChanged(state: Int) {}
})
}
private fun refreshCurrentCategoryFragment(position: Int, categoryName: String) {
val fragmentTag = "android:switcher:${vb.viewPager.id}:$position"
val currentFragment = childFragmentManager.findFragmentByTag(fragmentTag)
if (currentFragment is CategoryItemContentFragment) {
currentFragment.refreshCategoryData(categoryName)
}
}
}

View File

@ -0,0 +1,180 @@
package com.app.intelligent.board.uifragment
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.provider.Settings
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
import com.app.intelligent.board.AppContextHelper
import com.app.intelligent.board.R
import com.app.intelligent.board.databinding.KeyboardActivationDialogBinding
import com.app.intelligent.board.utils.AppCommonUtils
class KeyboardActivationDialog : DialogFragment() {
private lateinit var vb: KeyboardActivationDialogBinding
private lateinit var layoutStepOne: LinearLayout
private lateinit var layoutStepTwo: LinearLayout
private lateinit var imgStepOkOne: ImageView
private lateinit var imgStepOkTwo: ImageView
private lateinit var intentFilter: IntentFilter
private var myreceiver: BroadcastReceiver? = null
private lateinit var stepOne: TextView
private lateinit var stepTwo: TextView
private lateinit var context: Context
private var clickAction: (() -> Unit )? = null
companion object {
fun newInstance(): KeyboardActivationDialog {
val fragment = KeyboardActivationDialog()
return fragment
}
}
fun setClickListener(action:() -> Unit){
clickAction = action
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
vb = KeyboardActivationDialogBinding.inflate(layoutInflater)
context = AppContextHelper.Companion.appInstance
findViewId()
onViewStep()
getReceiver()
return vb.root
}
override fun onStart() {
super.onStart()
dialog?.run {
setCanceledOnTouchOutside(true)
window?.run {
setGravity(Gravity.BOTTOM)
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
attributes = attributes.apply {
width = WindowManager.LayoutParams.MATCH_PARENT
height = WindowManager.LayoutParams.WRAP_CONTENT
}
}
}
}
private fun findViewId() {
layoutStepOne = vb.linearStepOne
layoutStepTwo = vb.linearStepTwo
imgStepOkOne = vb.okOne
imgStepOkTwo = vb.okTwo
stepOne = vb.textStepOne
stepTwo = vb.textStepTwo
}
private fun onViewStep() {
layoutStepOne.setOnClickListener {
startActivity(Intent(Settings.ACTION_INPUT_METHOD_SETTINGS))
}
layoutStepTwo.setOnClickListener {
val inputMethodManager =
context.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.showInputMethodPicker()
}
vb.imClose.setOnClickListener {
dismiss()
}
}
override fun onResume() {
super.onResume()
updateUI()
}
private fun getReceiver() {
myreceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
updateUI()
}
}
intentFilter = IntentFilter(Intent.ACTION_INPUT_METHOD_CHANGED)
context.registerReceiver(myreceiver, intentFilter)
}
private fun updateUI() {
val checkEnable = AppCommonUtils.checkEnable(AppContextHelper.Companion.appInstance)
val checkSetDefault = AppCommonUtils.checkSetDefault(AppContextHelper.Companion.appInstance)
if (checkEnable && checkSetDefault) {
clickAction?.invoke()
dismiss()
return
}
if (checkEnable) {
layoutStepOne.isEnabled = false
layoutStepOne.isSelected = true
imgStepOkOne.isVisible = true
stepOne.setTextColor(context.getColor(R.color.step_true))
} else {
layoutStepOne.isEnabled = true
layoutStepOne.isSelected = false
imgStepOkOne.isVisible = false
stepOne.setTextColor(context.getColor(R.color.white))
}
if (checkSetDefault) {
layoutStepTwo.isEnabled = false
layoutStepTwo.isSelected = true
imgStepOkTwo.isVisible = true
stepTwo.setTextColor(context.getColor(R.color.step_true))
} else {
layoutStepTwo.isEnabled = true
layoutStepTwo.isSelected = false
imgStepOkTwo.isVisible = false
stepTwo.setTextColor(context.getColor(R.color.white))
}
}
override fun onDestroy() {
super.onDestroy()
if (myreceiver != null) {
context.unregisterReceiver(myreceiver)
}
}
}

View File

@ -0,0 +1,324 @@
package com.app.intelligent.board.uikeyboard;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.inputmethod.EditorInfo;
import androidx.core.graphics.drawable.DrawableCompat;
import com.app.intelligent.board.AppContextHelper;
import com.app.intelligent.board.R;
import com.app.intelligent.board.uikeyboardcore.CustomKeyCore;
import com.app.intelligent.board.uikeyboardcore.CustomKeyLayout;
import com.app.intelligent.board.utils.KeyboardKeyConst;
import com.app.intelligent.board.utils.KeyboardThemeManager;
import java.util.ArrayList;
import java.util.List;
public class CustomKeyboardKeyView extends CustomKeyLayout {
private Paint mPaint;
private Context mContext;
private float mRation = 0.5f;
//0 小写 1 大写 2 大写锁定
private int isLowerCase = 0;
//0 默认键盘 1 字母键盘 2 符号键盘
private int mMode = 0;
private KeyboardThemeManager themesManager;
private int curImeAction = EditorInfo.IME_ACTION_UNSPECIFIED;
public CustomKeyboardKeyView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
setAttribute(attrs, context);
}
public CustomKeyboardKeyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
setAttribute(attrs, context);
}
public CustomKeyboardKeyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mContext = context;
setAttribute(attrs, context);
}
public void setMode(int mType) {
this.mMode = mType;
}
public int getMode() {
return mMode;
}
public int isLowerCase() {
return isLowerCase;
}
public void setLowerCase(int lowerCase) {
isLowerCase = lowerCase;
}
public void updateUi(int ime) {
Log.d(AppContextHelper.TAG, "----------ime=" + ime);
curImeAction = ime;
themesManager.updateSkinConfig();
invalidate();
}
private void initPaint() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextAlign(Paint.Align.CENTER);
}
private void setAttribute(AttributeSet attrs, Context con) {
themesManager = new KeyboardThemeManager(con);
initPaint();
TypedArray mTypedArray = con.obtainStyledAttributes(attrs, R.styleable.CustomInputView);
// int color = mTypedArray.getColor(R.styleable.CustomInputView_text_color_done, 1);
//
// Drawable drawable = mTypedArray.getDrawable(R.styleable.CustomInputView_drawable_cancel);
//
// int textSize = mTypedArray.getInt(R.styleable.CustomInputView_text_size_key, 12);
mTypedArray.recycle();
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
KeyboardActionResponseConfig config = themesManager.getConfig();
List<KeyboardKeyItemModel> keyModels = new ArrayList<>();
int i = 0;
for (CustomKeyCore.Key curKey : getKeyboard().getKeys()) {
int code = curKey.codes[0];
if (config != null&& !config.getLayouts().isEmpty()) {
if (code == 113 ||code == 81 || code == 49||code == 91) {
i = 0;
KeyboardResponseAreaLayout responseLayout = config.getLayouts().get(0);
keyModels = responseLayout.getKeys();
} else if (code == 97||code == 65 || code == 33||code == 126) {
i = 0;
KeyboardResponseAreaLayout responseLayout = config.getLayouts().get(1);
keyModels = responseLayout.getKeys();
}else if (code == -1 || code == -103||code==-101) {
i = 0;
KeyboardResponseAreaLayout responseLayout = config.getLayouts().get(2);
keyModels = responseLayout.getKeys();
}else if (code == -2 || code == -102) {
i = 0;
KeyboardResponseAreaLayout responseLayout = config.getLayouts().get(3);
keyModels = responseLayout.getKeys();
}
String background = keyModels.get(i).getBackground()+".9.png";
i++;
Drawable configBg = themesManager.getConfigBg(background);
realNewDraw(configBg, curKey, canvas, code);
} else {
realDraw(curKey, canvas, code);
}
}
}
private void realNewDraw(Drawable configBg, CustomKeyCore.Key curKey, Canvas canvas, int code) {
switch (code) {
case KeyboardKeyConst.KEY_CODE_SHIFT:
// drawAllShift(curKey, canvas);
onDrawCurKey(curKey, canvas, "Shift", configBg, null);
break;
case KeyboardKeyConst.KEY_CODE_NUMBER_SHIFT:
onDrawCurKey(curKey, canvas, "More", configBg, null);
break;
case KeyboardKeyConst.KEY_CODE_DELETE:
onDrawCurKey(curKey, canvas, "Delete", configBg, null);
break;
case KeyboardKeyConst.KEY_CODE_SYMBOL_SHIFT:
onDrawCurKey(curKey, canvas, "123", configBg, null);
break;
case KeyboardKeyConst.KEY_CODE_CHANGE_NUMBER:
onDrawCurKey(curKey, canvas, null, configBg, null);
break;
case KeyboardKeyConst.KEY_CODE_BACK:
onDrawCurKey(curKey, canvas, "Back", configBg, null);
break;
case KeyboardKeyConst.KEY_CODE_SPACE:
onDrawCurKey(curKey, canvas, null, configBg, null);
break;
case KeyboardKeyConst.KEY_CODE_COMPLETE, KeyboardKeyConst.KEY_CODE_CANCEL:
Log.d(AppContextHelper.TAG, "-11111111111---------curImeAction=" + curImeAction);
if (curImeAction == EditorInfo.IME_ACTION_SEARCH) {
onDrawCurKey(curKey, canvas, "Search", configBg, null);
} else {
onDrawCurKey(curKey, canvas, "Done", configBg, null);
}
break;
default:
onDrawCurKey(curKey, canvas, null, configBg, null);
break;
}
}
private void realDraw(CustomKeyCore.Key curKey, Canvas canvas, int code) {
switch (code) {
case KeyboardKeyConst.KEY_CODE_SHIFT:
drawAllShift(curKey, canvas);
break;
case KeyboardKeyConst.KEY_CODE_NUMBER_SHIFT:
onDrawCurKey(curKey, canvas, "More", themesManager.getFunctionDraw(), null);
break;
case KeyboardKeyConst.KEY_CODE_DELETE:
onDrawCurKey(curKey, canvas, "Delete", themesManager.getFunctionDraw(), null);
break;
case KeyboardKeyConst.KEY_CODE_SYMBOL_SHIFT:
onDrawCurKey(curKey, canvas, "123", themesManager.getFunctionDraw(), null);
break;
case KeyboardKeyConst.KEY_CODE_CHANGE_NUMBER:
onDrawCurKey(curKey, canvas, null, themesManager.getToDraw(), null);
break;
case KeyboardKeyConst.KEY_CODE_BACK:
onDrawCurKey(curKey, canvas, "Back", themesManager.getToDraw(), null);
break;
case KeyboardKeyConst.KEY_CODE_SPACE:
onDrawCurKey(curKey, canvas, null, themesManager.getSpaceDraw(), null);
break;
case KeyboardKeyConst.KEY_CODE_COMPLETE, KeyboardKeyConst.KEY_CODE_CANCEL:
Log.d(AppContextHelper.TAG, "-11111111111---------curImeAction=" + curImeAction);
if (curImeAction == EditorInfo.IME_ACTION_SEARCH) {
onDrawCurKey(curKey, canvas, "Search", themesManager.getFunctionDraw(), null);
} else {
onDrawCurKey(curKey, canvas, "Done", themesManager.getFunctionDraw(), null);
}
break;
default:
onDrawCurKey(curKey, canvas, null, themesManager.getGeneralDraw(), null);
break;
}
}
private void drawAllShift(CustomKeyCore.Key curKey, Canvas canvas) {
if (isLowerCase == 0) {
onDrawCurKey(curKey, canvas, "Shift", themesManager.getFunctionDraw(), null);
} else if (isLowerCase == 1) {
onDrawCurKey(curKey, canvas, "Shift", themesManager.getFunctionDraw(), null);
} else if (isLowerCase == 2) {
onDrawCurKey(curKey, canvas, "Shift", themesManager.getFunctionDraw(), null);
}
}
private void onDrawCurKey(CustomKeyCore.Key curKey, Canvas curCanvas, String label, Drawable bgDrawable, Drawable iconDraw) {
if (bgDrawable != null) {
onDrawKeyBackground(curKey, curCanvas, bgDrawable);
}
if (iconDraw != null) {
onDrawKeyIcon(curKey, curCanvas, iconDraw);
}
onDrawKeyText(curKey, curCanvas, label);
}
private void onDrawKeyText(CustomKeyCore.Key curKey, Canvas curCanvas, String label) {
mPaint.setColor(themesManager.getKeyTextColor());
mPaint.setTextSize(mContext.getResources().getDimension(R.dimen.text_size));
float v = curKey.width / 2f;
float v1 = curKey.height / 2f;
float v2 = (mPaint.getTextSize() - mPaint.descent()) / 2f;
if (curKey.label != null) {
curCanvas.drawText((String) curKey.label, curKey.x + getPaddingLeft() + v, curKey.y + getPaddingRight() + v1 + v2, mPaint);
} else if (label != null) {
curCanvas.drawText(label, curKey.x + getPaddingLeft() + v, curKey.y + getPaddingRight() + v1 + v2, mPaint);
}
}
private void onDrawKeyBackground(CustomKeyCore.Key curKey, Canvas curCanvas, Drawable curDrawable) {
if (curKey.codes[0] != 0) {
curDrawable.setState(curKey.getCurrentDrawableState());
}
Rect rect = new Rect((curKey.x + this.getPaddingLeft()), (curKey.y + this.getPaddingTop()), (curKey.x + this.getPaddingLeft() + curKey.width), (curKey.y + this.getPaddingTop() + curKey.height));
curDrawable.setBounds(rect);
curDrawable.draw(curCanvas);
}
private void onDrawKeyIcon(CustomKeyCore.Key curKey, Canvas curCanvas, Drawable curDrawable) {
Drawable wrap = DrawableCompat.wrap(curDrawable);
curKey.icon = curDrawable;
float iconW = (float) curKey.icon.getIntrinsicWidth();
float iconH = (float) curKey.icon.getIntrinsicHeight();
float wDivRation = iconW / curKey.width;
float hDivRation = iconH / curKey.height;
curKey.icon.draw(curCanvas);
if (wDivRation > hDivRation) {
float minRatio = 0;
if (wDivRation <= mRation) {
minRatio = wDivRation;
} else {
minRatio = mRation;
}
iconH = (iconH / wDivRation) * minRatio;
iconW = (iconW / wDivRation) * minRatio;
} else {
float minRatio = 0;
if (hDivRation <= mRation) {
minRatio = hDivRation;
} else {
minRatio = mRation;
}
iconH = (iconH / hDivRation) * minRatio;
iconW = (iconW / hDivRation) * minRatio;
}
float subW = (curKey.width - iconW) / 2f;
float subH = (curKey.height - iconH) / 2f;
int xLeft = (int) (curKey.x + getPaddingLeft() + subW);
int yTop = (int) (curKey.y + getPaddingTop() + subH);
int xRight = (int) (xLeft + iconW);
int yBottom = (int) (yTop + iconH);
curKey.icon.setBounds(xLeft, yTop, xRight, yBottom);
curKey.icon.draw(curCanvas);
}
}

View File

@ -0,0 +1,168 @@
package com.app.intelligent.board.uikeyboard;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
public class KeyboardActionResponseConfig {
private String version;
private String supportLayouts;
private int hideHint;
private String layoutStyle;
private List<KeyboardResponseAreaLayout> layouts = new ArrayList<>();
private List<KeyboardKeyItemModel> keyModelList = new ArrayList<>();
private LinkedHashMap<String, String> maps = new LinkedHashMap<>();
private String KeyDefault;
private String KeyMarkDefault;
private String KeyFuncDefault;
private String KeyShift;
private String KeyDelete;
private String KeyAlphaSymbol;
private String KeyEmoji;
private String KeyMark;
private String KeySpace;
private String KeyEnter;
// Getters and Setters
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getSupportLayouts() {
return supportLayouts;
}
public void setSupportLayouts(String supportLayouts) {
this.supportLayouts = supportLayouts;
}
public int getHideHint() {
return hideHint;
}
public void setHideHint(int hideHint) {
this.hideHint = hideHint;
}
public String getLayoutStyle() {
return layoutStyle;
}
public void setLayoutStyle(String layoutStyle) {
this.layoutStyle = layoutStyle;
}
public List<KeyboardResponseAreaLayout> getLayouts() {
return layouts;
}
public void addLayout(KeyboardResponseAreaLayout layout) {
this.layouts.add(layout);
}
public List<KeyboardKeyItemModel> getKeyList() {
return keyModelList;
}
public KeyboardKeyItemModel getLastKeyList() {
return keyModelList.isEmpty() ? null : keyModelList.get(keyModelList.size() - 1);
}
public void addKey(KeyboardKeyItemModel keyModel) {
this.keyModelList.add(keyModel);
}
public LinkedHashMap<String, String> getMaps() {
return maps;
}
public void setMaps(LinkedHashMap<String, String> maps) {
this.maps = maps;
}
public String getKeyDefault() {
return KeyDefault;
}
public void setKeyDefault(String keyDefault) {
KeyDefault = keyDefault;
}
public String getKeyMarkDefault() {
return KeyMarkDefault;
}
public void setKeyMarkDefault(String keyMarkDefault) {
KeyMarkDefault = keyMarkDefault;
}
public String getKeyFuncDefault() {
return KeyFuncDefault;
}
public void setKeyFuncDefault(String keyFuncDefault) {
KeyFuncDefault = keyFuncDefault;
}
public String getKeyShift() {
return KeyShift;
}
public void setKeyShift(String keyShift) {
KeyShift = keyShift;
}
public String getKeyDelete() {
return KeyDelete;
}
public void setKeyDelete(String keyDelete) {
KeyDelete = keyDelete;
}
public String getKeyAlphaSymbol() {
return KeyAlphaSymbol;
}
public void setKeyAlphaSymbol(String keyAlphaSymbol) {
KeyAlphaSymbol = keyAlphaSymbol;
}
public String getKeyEmoji() {
return KeyEmoji;
}
public void setKeyEmoji(String keyEmoji) {
KeyEmoji = keyEmoji;
}
public String getKeyMark() {
return KeyMark;
}
public void setKeyMark(String keyMark) {
KeyMark = keyMark;
}
public String getKeySpace() {
return KeySpace;
}
public void setKeySpace(String keySpace) {
KeySpace = keySpace;
}
public String getKeyEnter() {
return KeyEnter;
}
public void setKeyEnter(String keyEnter) {
KeyEnter = keyEnter;
}
}

View File

@ -0,0 +1,285 @@
package com.app.intelligent.board.uikeyboard;
import android.annotation.SuppressLint;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.InputMethodService;
import android.media.MediaPlayer;
import android.util.Log;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.ImageView;
import android.widget.VideoView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.bumptech.glide.Glide;
import com.bumptech.glide.integration.webp.decoder.WebpDrawable;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.app.intelligent.board.AppContextHelper;
import com.app.intelligent.board.R;
import com.app.intelligent.board.utils.KeyboardKeyConst;
import com.app.intelligent.board.utils.AppCommonUtils;
import com.app.intelligent.board.utils.KeyboardThemeSaver;
import com.app.intelligent.board.uikeyboardcore.CustomKeyCore;
import com.app.intelligent.board.uikeyboardcore.CustomKeyLayout;
import kotlin.Unit;
import kotlin.jvm.functions.Function2;
public class KeyboardCustomInputMethodService extends InputMethodService implements CustomKeyLayout.OnKeyboardActionListener {
private CustomKeyboardKeyView myKeyBoardView;
private CustomKeyCore mKeyBoard;
private View parentView;
private ImageView imBG;
private VideoView videoView;
private int a = R.xml.xml_one;
private int b= R.xml.xml_two;
private int c = R.xml.xml_three;
private int curImeAction = EditorInfo.IME_ACTION_UNSPECIFIED;
@SuppressLint("InflateParams")
@Override
public View onCreateInputView() {
parentView = getLayoutInflater().inflate(R.layout.layout_keyboard_input_view, null);
findView();
return parentView;
}
private void findView() {
imBG = parentView.findViewById(R.id.gif_bg);
videoView = parentView.findViewById(R.id.video_view);
mKeyBoard = new CustomKeyCore(this, a);
myKeyBoardView = parentView.findViewById(R.id.custom_input_view);
myKeyBoardView.setEnabled(true);
myKeyBoardView.setPreviewEnabled(false);
myKeyBoardView.setKeyboard(mKeyBoard);
myKeyBoardView.setOnKeyboardActionListener(this);
}
@Override
public void onWindowHidden() {
super.onWindowHidden();
if(videoView.isPlaying()){
videoView.pause();
}
}
@Override
public void onWindowShown() {
super.onWindowShown();
EditorInfo currentInputEditorInfo = getCurrentInputEditorInfo();
curImeAction = AppCommonUtils.INSTANCE.getTextForImeAction(currentInputEditorInfo.imeOptions);
String skinPath = KeyboardThemeSaver.INSTANCE.getSkinPath();
if(skinPath == null || skinPath.isEmpty()){
Log.d(AppContextHelper.TAG, "---------skinPath= bull");
myKeyBoardView.updateUi(curImeAction);
}else {
Log.d(AppContextHelper.TAG, "---------skinPath= 1111");
KeyboardOperationHelper.INSTANCE.readBgOrVideo(this, new Function2<String, Drawable, Unit>() {
@Override
public Unit invoke(String s, Drawable drawable) {
Log.d(AppContextHelper.TAG, "---------s= "+s+"---------drawable="+drawable);
if (s != null) {
myKeyBoardView.setBackground(null);
if(s.endsWith(".gif")){
imBG.setVisibility(View.VISIBLE);
videoView.setVisibility(View.GONE);
Glide.with(KeyboardCustomInputMethodService.this)
.load(s)
.addListener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
if (resource instanceof WebpDrawable) {
((WebpDrawable) resource).setLoopCount(WebpDrawable.LOOP_FOREVER);
}
return false;
}
}).into(imBG) ;
}else if(s.endsWith(".mp4")){
imBG.setVisibility(View.GONE);
videoView.setVisibility(View.VISIBLE);
videoView.setVideoPath(s);
videoView.start();
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.setLooping(true);
}
});
}
} else {
myKeyBoardView.setBackground(drawable);
}
myKeyBoardView.updateUi(curImeAction);
return null;
}
});
}
}
@Override
public void onDestroy() {
super.onDestroy();
videoView.stopPlayback();
}
@Override
public void onPress(int primaryCode) {
}
@Override
public void onRelease(int primaryCode) {
}
@Override
public void onKey(int primaryCode, int[] keyCodes) {
InputConnection curInputConnect = getCurrentInputConnection();
switch (primaryCode) {
case KeyboardKeyConst.KEY_CODE_DELETE:
curInputConnect.deleteSurroundingText(1, 0);
break;
case KeyboardKeyConst.KEY_CODE_SHIFT:
switchShift();
break;
case KeyboardKeyConst.KEY_CODE_NUMBER_SHIFT:
case KeyboardKeyConst.KEY_CODE_SYMBOL_SHIFT:
switchMoreOrNumber();
break;
case KeyboardKeyConst.KEY_CODE_CHANGE_NUMBER:
case KeyboardKeyConst.KEY_CODE_BACK:
switchNormalOrNumber();
break;
case KeyboardKeyConst.KEY_CODE_COMPLETE:
case KeyboardKeyConst.KEY_CODE_CANCEL:
curInputConnect.performEditorAction(curImeAction);
// curInputConnect.performEditorAction(EditorInfo.IME_ACTION_DONE);
break;
default:
String codeToChar = KeyboardOperationHelper.INSTANCE.primaryCodeToChar(primaryCode);
curInputConnect.commitText(codeToChar, 1);
if (myKeyBoardView.isLowerCase() == 1) {
//自动转小写
myKeyBoardView.setLowerCase(0);
KeyboardOperationHelper.INSTANCE.keyToLowerCase(mKeyBoard);
myKeyBoardView.setKeyboard(mKeyBoard);
}
break;
}
}
private void switchMoreOrNumber() {
int mode = myKeyBoardView.getMode();
switch (mode) {
case 1:
mKeyBoard = new CustomKeyCore(this, c);
myKeyBoardView.setMode(2);
myKeyBoardView.setKeyboard(mKeyBoard);
break;
case 2:
mKeyBoard = new CustomKeyCore(this, b);
myKeyBoardView.setMode(1);
myKeyBoardView.setKeyboard(mKeyBoard);
break;
}
}
private void switchNormalOrNumber() {
int mode = myKeyBoardView.getMode();
switch (mode) {
case 0:
mKeyBoard = new CustomKeyCore(this, b);
myKeyBoardView.setMode(1);
myKeyBoardView.setKeyboard(mKeyBoard);
break;
case 1:
case 2:
mKeyBoard = new CustomKeyCore(this, a);
myKeyBoardView.setMode(0);
myKeyBoardView.setKeyboard(mKeyBoard);
break;
}
}
private void switchShift() {
int lowerCase = myKeyBoardView.isLowerCase();
switch (lowerCase) {
case 0:
//当前小写转大写
myKeyBoardView.setLowerCase(1);
KeyboardOperationHelper.INSTANCE.keyToUpper(mKeyBoard);
myKeyBoardView.setKeyboard(mKeyBoard);
break;
case 1:
//当前大写转锁定大写
myKeyBoardView.setLowerCase(2);
break;
case 2:
//当前锁定大写转小写
myKeyBoardView.setLowerCase(0);
KeyboardOperationHelper.INSTANCE.keyToLowerCase(mKeyBoard);
myKeyBoardView.setKeyboard(mKeyBoard);
break;
}
}
@Override
public void onText(CharSequence text) {
}
@Override
public void swipeLeft() {
}
@Override
public void swipeRight() {
}
@Override
public void swipeDown() {
}
@Override
public void swipeUp() {
}
}

View File

@ -0,0 +1,33 @@
package com.app.intelligent.board.uikeyboard;
// 按键对象模型
public class KeyboardKeyItemModel {
private String name;
private String background;
private String label;
public KeyboardKeyItemModel(String name) {
this.name = name;
}
// Getters and Setters
public String getName() {
return name;
}
public String getBackground() {
return background;
}
public void setBackground(String background) {
this.background = background;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
}

View File

@ -0,0 +1,100 @@
package com.app.intelligent.board.uikeyboard
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.util.Log
import com.app.intelligent.board.AppContextHelper
import com.app.intelligent.board.uikeyboardcore.CustomKeyCore
import com.app.intelligent.board.utils.KeyboardKeyConst
import com.app.intelligent.board.utils.KeyboardThemeSaver
import java.io.File
object KeyboardOperationHelper {
fun keyToUpper(mKeyBoard: CustomKeyCore) {
for (key in mKeyBoard.keys) {
if (key.label != null) {
if (key.label.length == 1) {
val charLabel = key.label.toString()[0]
val toUpperCase = charLabel.uppercaseChar()
key.codes[0] = toUpperCase.toInt()
key.label = toUpperCase.toString()
}
}
}
}
fun keyToLowerCase(mKeyBoard: CustomKeyCore) {
for (key in mKeyBoard.keys) {
if (key.label != null) {
if (key.label.length == 1) {
val charLabel = key.label.toString()[0]
val toLowerCase = charLabel.lowercaseChar()
key.codes[0] = toLowerCase.toInt()
key.label = toLowerCase.toString()
}
}
}
}
fun primaryCodeToChar(primCode: Int): String {
val toString = primCode.toChar().toString()
return toString
}
@SuppressLint("SuspiciousIndentation")
fun readBgOrVideo(
context: Context,
playAction: (gif: String?, bgDraw: Drawable?) -> Unit
) {
KeyboardThemeSaver.getSkinPath()?.let { resPath ->
val videoPath = "${resPath}res/raw/${KeyboardKeyConst.videoName}"
val videoPath2 = "${resPath}res/raw/${KeyboardKeyConst.video}"
val backgroundPath = "${resPath}res/drawable-xxhdpi-v4/${KeyboardKeyConst.bgName}"
val backgroundPath_png = "${resPath}res/drawable-xxhdpi-v4/${KeyboardKeyConst.bgName_png}"
val file = File(videoPath)
val file2 = File(videoPath2)
val file3 = File(backgroundPath)
val file4 = File(backgroundPath_png)
if (file.exists() || file2.exists()) {
Log.d(AppContextHelper.Companion.TAG, "--------11111111= resPath=${resPath}")
val path: String = if (file.exists()) {
videoPath
} else {
videoPath2
}
val bitmapDrawable =
BitmapDrawable(context.resources, BitmapFactory.decodeFile(path))
playAction.invoke(path, null)
// playAction.invoke(mediaPlayer,null);
} else {
Log.d(
AppContextHelper.Companion.TAG,
"--------11111111= file3.exists()" + file3.exists() + "---resPath=" + resPath
)
if (file3.exists()) {
val bitmapDrawable =
BitmapDrawable(context.resources, BitmapFactory.decodeFile(backgroundPath))
playAction.invoke(null, bitmapDrawable)
} else if (file4.exists()) {
val bitmapDrawable =
BitmapDrawable(context.resources, BitmapFactory.decodeFile(backgroundPath_png))
playAction.invoke(null, bitmapDrawable)
} else {
playAction.invoke(null, null)
}
}
}
}
}

View File

@ -0,0 +1,35 @@
package com.app.intelligent.board.uikeyboard;
import java.util.ArrayList;
import java.util.List;
public class KeyboardResponseAreaLayout {
private String name;
private List<KeyboardKeyItemModel> keyModels = new ArrayList<>();
public KeyboardResponseAreaLayout(String name) {
this.name = name;
}
// Getters and Setters
public String getName() {
return name;
}
public List<KeyboardKeyItemModel> getKeys() {
return keyModels;
}
public void addKey(KeyboardKeyItemModel keyModel) {
this.keyModels.add(keyModel);
}
public KeyboardKeyItemModel getLastKey() {
return keyModels.isEmpty() ? null : keyModels.get(keyModels.size() - 1);
}
}

View File

@ -0,0 +1,858 @@
package com.app.intelligent.board.uikeyboardcore;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.util.Xml;
import androidx.annotation.XmlRes;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import com.app.intelligent.board.R;
public class CustomKeyCore {
static final String TAG = "------------Keyboard-----------";
// Keyboard XML Tags
private static final String TAG_KEYBOARD = "Keyboard";
private static final String TAG_ROW = "Row";
private static final String TAG_KEY = "Key";
public static final int EDGE_LEFT = 0x01;
public static final int EDGE_RIGHT = 0x02;
public static final int EDGE_TOP = 0x04;
public static final int EDGE_BOTTOM = 0x08;
public static final int KEYCODE_SHIFT = -1;
public static final int KEYCODE_MODE_CHANGE = -2;
public static final int KEYCODE_CANCEL = -3;
public static final int KEYCODE_DONE = -4;
public static final int KEYCODE_DELETE = -5;
public static final int KEYCODE_ALT = -6;
/** Keyboard label **/
private CharSequence mLabel;
/** Horizontal gap default for all rows */
private int mDefaultHorizontalGap;
/** Default key width */
private int mDefaultWidth;
/** Default key height */
private int mDefaultHeight;
/** Default gap between rows */
private int mDefaultVerticalGap;
/** Is the keyboard in the shifted state */
private boolean mShifted;
/** Key instance for the shift key, if present */
private Key[] mShiftKeys = { null, null };
/** Key index for the shift key, if present */
private int[] mShiftKeyIndices = {-1, -1};
/** Current key width, while loading the keyboard */
private int mKeyWidth;
/** Current key height, while loading the keyboard */
private int mKeyHeight;
/** Total height of the keyboard, including the padding and keys */
private int mTotalHeight;
/**
* Total width of the keyboard, including left side gaps and keys, but not any gaps on the
* right side.
*/
private int mTotalWidth;
/** List of keys in this keyboard */
private List<Key> mKeys;
/** List of modifier keys such as Shift & Alt, if any */
private List<Key> mModifierKeys;
/** Width of the screen available to fit the keyboard */
private int mDisplayWidth;
/** Height of the screen */
private int mDisplayHeight;
/** Keyboard mode, or zero, if none. */
private int mKeyboardMode;
// Variables for pre-computing nearest keys.
private static final int GRID_WIDTH = 10;
private static final int GRID_HEIGHT = 5;
private static final int GRID_SIZE = GRID_WIDTH * GRID_HEIGHT;
private int mCellWidth;
private int mCellHeight;
private int[][] mGridNeighbors;
private int mProximityThreshold;
/** Number of key widths from current touch point to search for nearest keys. */
private static float SEARCH_DISTANCE = 1.8f;
private ArrayList<Row> rows = new ArrayList<>();
/**
* Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
* Some of the key size defaults can be overridden per row from what the {@link CustomKeyCore}
* defines.
* @attr ref android.R.styleable#King_Keyboard_keyWidth
* @attr ref android.R.styleable#King_Keyboard_keyHeight
* @attr ref android.R.styleable#King_Keyboard_horizontalGap
* @attr ref android.R.styleable#King_Keyboard_verticalGap
* @attr ref android.R.styleable#King_Keyboard_Row_rowEdgeFlags
* @attr ref android.R.styleable#King_Keyboard_Row_keyboardMode
*/
public static class Row {
/** Default width of a key in this row. */
public int defaultWidth;
/** Default height of a key in this row. */
public int defaultHeight;
/** Default horizontal gap between keys in this row. */
public int defaultHorizontalGap;
/** Vertical gap following this row. */
public int verticalGap;
ArrayList<Key> mKeys = new ArrayList<>();
/**
* Edge flags for this row of keys. Possible values that can be assigned are
* {@link CustomKeyCore#EDGE_TOP EDGE_TOP} and {@link CustomKeyCore#EDGE_BOTTOM EDGE_BOTTOM}
*/
public int rowEdgeFlags;
/** The keyboard mode for this row */
public int mode;
private CustomKeyCore parent;
public Row(CustomKeyCore parent) {
this.parent = parent;
}
public Row(Resources res, CustomKeyCore parent, XmlResourceParser parser) {
this.parent = parent;
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.My_Keyboard_view);
defaultWidth = getDimensionOrFraction(a,
R.styleable.My_Keyboard_view_android_keyWidth,
parent.mDisplayWidth, parent.mDefaultWidth);
defaultHeight = getDimensionOrFraction(a,
R.styleable.My_Keyboard_view_android_keyHeight,
parent.mDisplayHeight, parent.mDefaultHeight);
defaultHorizontalGap = getDimensionOrFraction(a,
R.styleable.My_Keyboard_view_android_horizontalGap,
parent.mDisplayWidth, parent.mDefaultHorizontalGap);
verticalGap = getDimensionOrFraction(a,
R.styleable.My_Keyboard_view_android_verticalGap,
parent.mDisplayHeight, parent.mDefaultVerticalGap);
a.recycle();
a = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Kil_Keyboard_Row);
rowEdgeFlags = a.getInt(R.styleable.Kil_Keyboard_Row_android_rowEdgeFlags, 0);
mode = a.getResourceId(R.styleable.Kil_Keyboard_Row_android_keyboardMode,
0);
}
}
/**
* Class for describing the position and characteristics of a single key in the keyboard.
*
* @attr ref android.R.styleable#King_Keyboard_keyWidth
* @attr ref android.R.styleable#King_Keyboard_keyHeight
* @attr ref android.R.styleable#King_Keyboard_horizontalGap
* @attr ref android.R.styleable#King_Keyboard_Key_codes
* @attr ref android.R.styleable#King_Keyboard_Key_keyIcon
* @attr ref android.R.styleable#King_Keyboard_Key_keyLabel
* @attr ref android.R.styleable#King_Keyboard_Key_iconPreview
* @attr ref android.R.styleable#King_Keyboard_Key_isSticky
* @attr ref android.R.styleable#King_Keyboard_Key_isRepeatable
* @attr ref android.R.styleable#King_Keyboard_Key_isModifier
* @attr ref android.R.styleable#King_Keyboard_Key_popupKeyboard
* @attr ref android.R.styleable#King_Keyboard_Key_popupCharacters
* @attr ref android.R.styleable#King_Keyboard_Key_keyOutputText
* @attr ref android.R.styleable#King_Keyboard_Key_keyEdgeFlags
*/
public static class Key {
/**
* All the key codes (unicode or custom code) that this key could generate, zero'th
* being the most important.
*/
public int[] codes;
/** Label to display */
public CharSequence label;
/** Icon to display instead of a label. Icon takes precedence over a label */
public Drawable icon;
/** Preview version of the icon, for the preview popup */
public Drawable iconPreview;
/** Width of the key, not including the gap */
public int width;
/** Height of the key, not including the gap */
public int height;
/** The horizontal gap before this key */
public int gap;
/** Whether this key is sticky, i.e., a toggle key */
public boolean sticky;
/** X coordinate of the key in the keyboard layout */
public int x;
/** Y coordinate of the key in the keyboard layout */
public int y;
/** The current pressed state of this key */
public boolean pressed;
/** If this is a sticky key, is it on? */
public boolean on;
/** Text to output when pressed. This can be multiple characters, like ".com" */
public CharSequence text;
/** Popup characters */
public CharSequence popupCharacters;
/**
* Flags that specify the anchoring to edges of the keyboard for detecting touch events
* that are just out of the boundary of the key. This is a bit mask of
* {@link CustomKeyCore#EDGE_LEFT}, {@link CustomKeyCore#EDGE_RIGHT}, {@link CustomKeyCore#EDGE_TOP} and
* {@link CustomKeyCore#EDGE_BOTTOM}.
*/
public int edgeFlags;
/** Whether this is a modifier key, such as Shift or Alt */
public boolean modifier;
/** The keyboard that this key belongs to */
private CustomKeyCore keyboard;
/**
* If this key pops up a mini keyboard, this is the resource id for the XML layout for that
* keyboard.
*/
public int popupResId;
/** Whether this key repeats itself when held down */
public boolean repeatable;
private final static int[] KEY_STATE_NORMAL_ON = {
android.R.attr.state_checkable,
android.R.attr.state_checked
};
private final static int[] KEY_STATE_PRESSED_ON = {
android.R.attr.state_pressed,
android.R.attr.state_checkable,
android.R.attr.state_checked
};
private final static int[] KEY_STATE_NORMAL_OFF = {
android.R.attr.state_checkable
};
private final static int[] KEY_STATE_PRESSED_OFF = {
android.R.attr.state_pressed,
android.R.attr.state_checkable
};
private final static int[] KEY_STATE_NORMAL = {
};
private final static int[] KEY_STATE_PRESSED = {
android.R.attr.state_pressed
};
/** Create an empty key with no attributes. */
public Key(Row parent) {
keyboard = parent.parent;
height = parent.defaultHeight;
width = parent.defaultWidth;
gap = parent.defaultHorizontalGap;
edgeFlags = parent.rowEdgeFlags;
}
/** Create a key with the given top-left coordinate and extract its attributes from
* the XML parser.
* @param res resources associated with the caller's context
* @param parent the row that this key belongs to. The row must already be attached to
* a {@link CustomKeyCore}.
* @param x the x coordinate of the top-left
* @param y the y coordinate of the top-left
* @param parser the XML parser containing the attributes for this key
*/
public Key(Resources res, Row parent, int x, int y, XmlResourceParser parser) {
this(parent);
this.x = x;
this.y = y;
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.My_Keyboard_view);
width = getDimensionOrFraction(a,
R.styleable.My_Keyboard_view_android_keyWidth,
keyboard.mDisplayWidth, parent.defaultWidth);
height = getDimensionOrFraction(a,
R.styleable.My_Keyboard_view_android_keyHeight,
keyboard.mDisplayHeight, parent.defaultHeight);
gap = getDimensionOrFraction(a,
R.styleable.My_Keyboard_view_android_horizontalGap,
keyboard.mDisplayWidth, parent.defaultHorizontalGap);
a.recycle();
a = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.K_Keyboard_Key);
this.x += gap;
TypedValue codesValue = new TypedValue();
a.getValue(R.styleable.K_Keyboard_Key_android_codes,
codesValue);
if (codesValue.type == TypedValue.TYPE_INT_DEC
|| codesValue.type == TypedValue.TYPE_INT_HEX) {
codes = new int[] { codesValue.data };
} else if (codesValue.type == TypedValue.TYPE_STRING) {
codes = parseCSV(codesValue.string.toString());
}
iconPreview = a.getDrawable(R.styleable.K_Keyboard_Key_android_iconPreview);
if (iconPreview != null) {
iconPreview.setBounds(0, 0, iconPreview.getIntrinsicWidth(),
iconPreview.getIntrinsicHeight());
}
popupCharacters = a.getText(
R.styleable.K_Keyboard_Key_android_popupCharacters);
popupResId = a.getResourceId(
R.styleable.K_Keyboard_Key_android_popupKeyboard, 0);
repeatable = a.getBoolean(
R.styleable.K_Keyboard_Key_android_isRepeatable, false);
modifier = a.getBoolean(
R.styleable.K_Keyboard_Key_android_isModifier, false);
sticky = a.getBoolean(
R.styleable.K_Keyboard_Key_android_isSticky, false);
edgeFlags = a.getInt(R.styleable.K_Keyboard_Key_android_keyEdgeFlags, 0);
edgeFlags |= parent.rowEdgeFlags;
icon = a.getDrawable(
R.styleable.K_Keyboard_Key_android_keyIcon);
if (icon != null) {
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
}
label = a.getText(R.styleable.K_Keyboard_Key_android_keyLabel);
text = a.getText(R.styleable.K_Keyboard_Key_android_keyOutputText);
if (codes == null && !TextUtils.isEmpty(label)) {
codes = new int[] { label.charAt(0) };
}
a.recycle();
}
/**
* Informs the key that it has been pressed, in case it needs to change its appearance or
* state.
* @see #onReleased(boolean)
*/
public void onPressed() {
pressed = !pressed;
}
/**
* Changes the pressed state of the key.
*
* <p>Toggled state of the key will be flipped when all the following conditions are
* fulfilled:</p>
*
* <ul>
* <li>This is a sticky key, that is, {@link #sticky} is {@code true}.
* <li>The parameter {@code inside} is {@code true}.
* <li>{@link android.os.Build.VERSION#SDK_INT} is greater than
* {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}.
* </ul>
*
* @param inside whether the finger was released inside the key. Works only on Android M and
* later. See the method document for details.
* @see #onPressed()
*/
public void onReleased(boolean inside) {
pressed = !pressed;
if (sticky && inside) {
on = !on;
}
}
int[] parseCSV(String value) {
int count = 0;
int lastIndex = 0;
if (value.length() > 0) {
count++;
while ((lastIndex = value.indexOf(",", lastIndex + 1)) > 0) {
count++;
}
}
int[] values = new int[count];
count = 0;
StringTokenizer st = new StringTokenizer(value, ",");
while (st.hasMoreTokens()) {
try {
values[count++] = Integer.parseInt(st.nextToken());
} catch (NumberFormatException nfe) {
}
}
return values;
}
/**
* Detects if a point falls inside this key.
* @param x the x-coordinate of the point
* @param y the y-coordinate of the point
* @return whether or not the point falls inside the key. If the key is attached to an edge,
* it will assume that all points between the key and the edge are considered to be inside
* the key.
*/
public boolean isInside(int x, int y) {
boolean leftEdge = (edgeFlags & EDGE_LEFT) > 0;
boolean rightEdge = (edgeFlags & EDGE_RIGHT) > 0;
boolean topEdge = (edgeFlags & EDGE_TOP) > 0;
boolean bottomEdge = (edgeFlags & EDGE_BOTTOM) > 0;
if ((x >= this.x || (leftEdge && x <= this.x + this.width))
&& (x < this.x + this.width || (rightEdge && x >= this.x))
&& (y >= this.y || (topEdge && y <= this.y + this.height))
&& (y < this.y + this.height || (bottomEdge && y >= this.y))) {
return true;
} else {
return false;
}
}
/**
* Returns the square of the distance between the center of the key and the given point.
* @param x the x-coordinate of the point
* @param y the y-coordinate of the point
* @return the square of the distance of the point from the center of the key
*/
public int squaredDistanceFrom(int x, int y) {
int xDist = this.x + width / 2 - x;
int yDist = this.y + height / 2 - y;
return xDist * xDist + yDist * yDist;
}
/**
* Returns the drawable state for the key, based on the current state and type of the key.
* @return the drawable state of the key.
* @see android.graphics.drawable.StateListDrawable#setState(int[])
*/
public int[] getCurrentDrawableState() {
int[] states = KEY_STATE_NORMAL;
if (on) {
if (pressed) {
states = KEY_STATE_PRESSED_ON;
} else {
states = KEY_STATE_NORMAL_ON;
}
} else {
if (sticky) {
if (pressed) {
states = KEY_STATE_PRESSED_OFF;
} else {
states = KEY_STATE_NORMAL_OFF;
}
} else {
if (pressed) {
states = KEY_STATE_PRESSED;
}
}
}
return states;
}
}
/**
* Creates a keyboard from the given xml key layout file.
* @param context the application or service context
* @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
*/
public CustomKeyCore(Context context, int xmlLayoutResId) {
this(context, xmlLayoutResId, 0);
}
/**
* Creates a keyboard from the given xml key layout file. Weeds out rows
* that have a keyboard mode defined but don't match the specified mode.
* @param context the application or service context
* @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
* @param modeId keyboard mode identifier
* @param width sets width of keyboard
* @param height sets height of keyboard
*/
public CustomKeyCore(Context context, @XmlRes int xmlLayoutResId, int modeId, int width,
int height) {
mDisplayWidth = width;
mDisplayHeight = height;
mDefaultHorizontalGap = 0;
mDefaultWidth = mDisplayWidth / 10;
mDefaultVerticalGap = 0;
mDefaultHeight = mDefaultWidth;
mKeys = new ArrayList<>();
mModifierKeys = new ArrayList<>();
mKeyboardMode = modeId;
loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
}
/**
* Creates a keyboard from the given xml key layout file. Weeds out rows
* that have a keyboard mode defined but don't match the specified mode.
* @param context the application or service context
* @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
* @param modeId keyboard mode identifier
*/
public CustomKeyCore(Context context, @XmlRes int xmlLayoutResId, int modeId) {
DisplayMetrics dm = context.getResources().getDisplayMetrics();
mDisplayWidth = dm.widthPixels;
mDisplayHeight = dm.heightPixels;
//Log.v(TAG, "keyboard's display metrics:" + dm);
mDefaultHorizontalGap = 0;
mDefaultWidth = mDisplayWidth / 10;
mDefaultVerticalGap = 0;
mDefaultHeight = mDefaultWidth;
mKeys = new ArrayList<>();
mModifierKeys = new ArrayList<>();
mKeyboardMode = modeId;
loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
}
public CustomKeyCore(Context context, int layoutTemplateResId,
CharSequence characters, int columns, int horizontalPadding) {
this(context, layoutTemplateResId);
int x = 0;
int y = 0;
int column = 0;
mTotalWidth = 0;
Row row = new Row(this);
row.defaultHeight = mDefaultHeight;
row.defaultWidth = mDefaultWidth;
row.defaultHorizontalGap = mDefaultHorizontalGap;
row.verticalGap = mDefaultVerticalGap;
row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM;
final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns;
for (int i = 0; i < characters.length(); i++) {
char c = characters.charAt(i);
if (column >= maxColumns
|| x + mDefaultWidth + horizontalPadding > mDisplayWidth) {
x = 0;
y += mDefaultVerticalGap + mDefaultHeight;
column = 0;
}
final Key key = new Key(row);
key.x = x;
key.y = y;
key.label = String.valueOf(c);
key.codes = new int[] { c };
column++;
x += key.width + key.gap;
mKeys.add(key);
row.mKeys.add(key);
if (x > mTotalWidth) {
mTotalWidth = x;
}
}
mTotalHeight = y + mDefaultHeight;
rows.add(row);
}
final void resize(int newWidth, int newHeight) {
int numRows = rows.size();
for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) {
Row row = rows.get(rowIndex);
int numKeys = row.mKeys.size();
int totalGap = 0;
int totalWidth = 0;
for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {
Key key = row.mKeys.get(keyIndex);
if (keyIndex > 0) {
totalGap += key.gap;
}
totalWidth += key.width;
}
if (totalGap + totalWidth > newWidth) {
int x = 0;
float scaleFactor = (float)(newWidth - totalGap) / totalWidth;
for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {
Key key = row.mKeys.get(keyIndex);
key.width *= scaleFactor;
key.x = x;
x += key.width + key.gap;
}
}
}
mTotalWidth = newWidth;
// TODO: This does not adjust the vertical placement according to the new size.
// The main problem in the previous code was horizontal placement/size, but we should
// also recalculate the vertical sizes/positions when we get this resize call.
}
public List<Key> getKeys() {
return mKeys;
}
public List<Key> getModifierKeys() {
return mModifierKeys;
}
protected int getHorizontalGap() {
return mDefaultHorizontalGap;
}
protected void setHorizontalGap(int gap) {
mDefaultHorizontalGap = gap;
}
protected int getVerticalGap() {
return mDefaultVerticalGap;
}
protected void setVerticalGap(int gap) {
mDefaultVerticalGap = gap;
}
protected int getKeyHeight() {
return mDefaultHeight;
}
protected void setKeyHeight(int height) {
mDefaultHeight = height;
}
protected int getKeyWidth() {
return mDefaultWidth;
}
protected void setKeyWidth(int width) {
mDefaultWidth = width;
}
/**
* Returns the total height of the keyboard
* @return the total height of the keyboard
*/
public int getHeight() {
return mTotalHeight;
}
public int getMinWidth() {
return mTotalWidth;
}
public boolean setShifted(boolean shiftState) {
for (Key shiftKey : mShiftKeys) {
if (shiftKey != null) {
shiftKey.on = shiftState;
}
}
if (mShifted != shiftState) {
mShifted = shiftState;
return true;
}
return false;
}
public boolean isShifted() {
return mShifted;
}
/**
* @hide
*/
public int[] getShiftKeyIndices() {
return mShiftKeyIndices;
}
public int getShiftKeyIndex() {
return mShiftKeyIndices[0];
}
private void computeNearestNeighbors() {
// Round-up so we don't have any pixels outside the grid
mCellWidth = (getMinWidth() + GRID_WIDTH - 1) / GRID_WIDTH;
mCellHeight = (getHeight() + GRID_HEIGHT - 1) / GRID_HEIGHT;
mGridNeighbors = new int[GRID_SIZE][];
int[] indices = new int[mKeys.size()];
final int gridWidth = GRID_WIDTH * mCellWidth;
final int gridHeight = GRID_HEIGHT * mCellHeight;
for (int x = 0; x < gridWidth; x += mCellWidth) {
for (int y = 0; y < gridHeight; y += mCellHeight) {
int count = 0;
for (int i = 0; i < mKeys.size(); i++) {
final Key key = mKeys.get(i);
if (key.squaredDistanceFrom(x, y) < mProximityThreshold ||
key.squaredDistanceFrom(x + mCellWidth - 1, y) < mProximityThreshold ||
key.squaredDistanceFrom(x + mCellWidth - 1, y + mCellHeight - 1)
< mProximityThreshold ||
key.squaredDistanceFrom(x, y + mCellHeight - 1) < mProximityThreshold) {
indices[count++] = i;
}
}
int [] cell = new int[count];
System.arraycopy(indices, 0, cell, 0, count);
mGridNeighbors[(y / mCellHeight) * GRID_WIDTH + (x / mCellWidth)] = cell;
}
}
}
/**
* Returns the indices of the keys that are closest to the given point.
* @param x the x-coordinate of the point
* @param y the y-coordinate of the point
* @return the array of integer indices for the nearest keys to the given point. If the given
* point is out of range, then an array of size zero is returned.
*/
public int[] getNearestKeys(int x, int y) {
if (mGridNeighbors == null) computeNearestNeighbors();
if (x >= 0 && x < getMinWidth() && y >= 0 && y < getHeight()) {
int index = (y / mCellHeight) * GRID_WIDTH + (x / mCellWidth);
if (index < GRID_SIZE) {
return mGridNeighbors[index];
}
}
return new int[0];
}
protected Row createRowFromXml(Resources res, XmlResourceParser parser) {
return new Row(res, this, parser);
}
protected Key createKeyFromXml(Resources res, Row parent, int x, int y,
XmlResourceParser parser) {
return new Key(res, parent, x, y, parser);
}
private void loadKeyboard(Context context, XmlResourceParser parser) {
boolean inKey = false;
boolean inRow = false;
boolean leftMostKey = false;
int row = 0;
int x = 0;
int y = 0;
Key key = null;
Row currentRow = null;
Resources res = context.getResources();
boolean skipRow = false;
try {
int event;
while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
if (event == XmlResourceParser.START_TAG) {
String tag = parser.getName();
if (TAG_ROW.equals(tag)) {
inRow = true;
x = 0;
currentRow = createRowFromXml(res, parser);
rows.add(currentRow);
skipRow = currentRow.mode != 0 && currentRow.mode != mKeyboardMode;
if (skipRow) {
skipToEndOfRow(parser);
inRow = false;
}
} else if (TAG_KEY.equals(tag)) {
inKey = true;
key = createKeyFromXml(res, currentRow, x, y, parser);
mKeys.add(key);
if (key.codes[0] == KEYCODE_SHIFT) {
// Find available shift key slot and put this shift key in it
for (int i = 0; i < mShiftKeys.length; i++) {
if (mShiftKeys[i] == null) {
mShiftKeys[i] = key;
mShiftKeyIndices[i] = mKeys.size()-1;
break;
}
}
mModifierKeys.add(key);
} else if (key.codes[0] == KEYCODE_ALT) {
mModifierKeys.add(key);
}
currentRow.mKeys.add(key);
} else if (TAG_KEYBOARD.equals(tag)) {
parseKeyboardAttributes(res, parser);
}
} else if (event == XmlResourceParser.END_TAG) {
if (inKey) {
inKey = false;
x += key.gap + key.width;
if (x > mTotalWidth) {
mTotalWidth = x;
}
} else if (inRow) {
inRow = false;
y += currentRow.verticalGap;
y += currentRow.defaultHeight;
row++;
} else {
// TODO: error or extend?
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
mTotalHeight = y - mDefaultVerticalGap;
}
private void skipToEndOfRow(XmlResourceParser parser)
throws XmlPullParserException, IOException {
int event;
while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
if (event == XmlResourceParser.END_TAG
&& parser.getName().equals(TAG_ROW)) {
break;
}
}
}
private void parseKeyboardAttributes(Resources res, XmlResourceParser parser) {
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.My_Keyboard_view);
mDefaultWidth = getDimensionOrFraction(a,
R.styleable.My_Keyboard_view_android_keyWidth,
mDisplayWidth, mDisplayWidth / 10);
mDefaultHeight = getDimensionOrFraction(a,
R.styleable.My_Keyboard_view_android_keyHeight,
mDisplayHeight, 50);
mDefaultHorizontalGap = getDimensionOrFraction(a,
R.styleable.My_Keyboard_view_android_horizontalGap,
mDisplayWidth, 0);
mDefaultVerticalGap = getDimensionOrFraction(a,
R.styleable.My_Keyboard_view_android_verticalGap,
mDisplayHeight, 0);
mProximityThreshold = (int) (mDefaultWidth * SEARCH_DISTANCE);
mProximityThreshold = mProximityThreshold * mProximityThreshold; // Square it for comparison
a.recycle();
}
static int getDimensionOrFraction(TypedArray a, int index, int base, int defValue) {
TypedValue value = a.peekValue(index);
if (value == null) return defValue;
if (value.type == TypedValue.TYPE_DIMENSION) {
return a.getDimensionPixelOffset(index, defValue);
} else if (value.type == TypedValue.TYPE_FRACTION) {
// Round it to avoid values like 47.9999 from getting truncated
return Math.round(a.getFraction(index, base, base, defValue));
}
return defValue;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
package com.app.intelligent.board.uilistener
import com.app.intelligent.board.uimodel.KeyboardItemInfo
interface FavoriteItemDeleteListener {
fun OnRemoveLike(data: KeyboardItemInfo)
}

View File

@ -0,0 +1,8 @@
package com.app.intelligent.board.uilistener
import java.io.File
interface KeyboardConfigChangeListener {
fun OnApplySkinListener(fileList: List<File?>?)
}

View File

@ -0,0 +1,6 @@
package com.app.intelligent.board.uilistener
interface KeyboardItemClickListener {
fun OnItemClickListener( )
}

View File

@ -0,0 +1,6 @@
package com.app.intelligent.board.uilistener
interface KeyboardViewExpandListener {
fun OnClickSeeAll(name: String)
}

View File

@ -0,0 +1,27 @@
package com.app.intelligent.board.uimodel;
import java.util.List;
public class KeyboardItemDataWrapper {
private String parentName;
private List<KeyboardItemInfo> keyboardList;
public String getParentName() {
return parentName;
}
public List<KeyboardItemInfo> getKeyboardList() {
return keyboardList;
}
public void setParentName(String name) {
this.parentName = name;
}
public void setKeyboardList(List<KeyboardItemInfo> keyboardList) {
this.keyboardList = keyboardList;
}
}

View File

@ -0,0 +1,87 @@
package com.app.intelligent.board.uimodel;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import java.io.Serializable;
@Entity
public class KeyboardItemInfo implements Serializable {
@PrimaryKey(autoGenerate = true)
private long id;
private String titleName;
private String thumbUrl;
private String thumbGif;
private String zipPath;
private String imgPath;
private String imgGif;
public void setImgGif(String imgGif) {
this.imgGif = imgGif;
}
public String getTitleName() {
return titleName;
}
public String getThumbUrl() {
return thumbUrl;
}
public String getThumbGif() {
return thumbGif;
}
public String getZipPath() {
return zipPath;
}
public String getImgPath() {
return imgPath;
}
public void setTitleName(String name) {
this.titleName = name;
}
public void setThumbUrl(String thumbUrl) {
this.thumbUrl = thumbUrl;
}
public void setThumbGif(String thumbGif) {
this.thumbGif = thumbGif;
}
public void setZipPath(String path) {
this.zipPath = path;
}
public void setImgPath(String imgPath) {
this.imgPath = imgPath;
}
public String getImgGif() {
return imgGif;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}

View File

@ -0,0 +1,115 @@
package com.app.intelligent.board.utils
import android.app.Activity
import android.content.Context
import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.provider.Settings
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.ImageView
import com.app.intelligent.board.AppContextHelper
import com.bumptech.glide.Glide
import com.bumptech.glide.integration.webp.decoder.WebpDrawable
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
import java.io.File
object AppCommonUtils {
val transform = RequestOptions().transform(CenterCrop(), RoundedCorners(dpToPx(8f)))
fun initFullScreen(activity: Activity, dark: Boolean? = true) {
val window = activity.window
val decorView = window.decorView
val rootView = decorView.rootView
//
if (dark == null) return
if (dark) {
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
)
} else {
decorView.setSystemUiVisibility(
(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
)
}
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
}
fun loadWepJif(mContext: Context, webpGifUrl: String, view: ImageView) {
Glide.with(mContext)
.load(webpGifUrl)
// .apply(transform)
.addListener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>,
isFirstResource: Boolean
): Boolean {
return false
}
override fun onResourceReady(
resource: Drawable,
model: Any,
target: Target<Drawable>,
dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
if (resource is WebpDrawable) {
resource.loopCount = WebpDrawable.LOOP_FOREVER
}
return false
}
}).into(view)
}
fun getBgDrawable(con: Context, filePath: String): Drawable? {
if (!File(filePath).exists()) {
return null
}
return BitmapDrawable(con.resources, BitmapFactory.decodeFile(filePath))
}
private val systemService =
AppContextHelper.Companion.appInstance.getSystemService(Context.INPUT_METHOD_SERVICE)
private val inputMethodManager = systemService as InputMethodManager
fun checkSetDefault(con: Context): Boolean {
val defaultId =
Settings.Secure.getString(con.contentResolver, Settings.Secure.DEFAULT_INPUT_METHOD)
return defaultId != null && defaultId.startsWith(con.packageName)
}
fun checkEnable(con: Context): Boolean {
for (methodInfo in inputMethodManager.enabledInputMethodList) {
if (methodInfo.id.startsWith(con.packageName)) {
return true
}
}
return false
}
fun getTextForImeAction(imeOptions: Int): Int {
return imeOptions and EditorInfo.IME_MASK_ACTION
}
fun dpToPx(dpValue: Float): Int {
val scale = AppContextHelper.Companion.appInstance.resources.displayMetrics.density
return (dpValue * scale + 0.5f).toInt()
}
}

View File

@ -0,0 +1,37 @@
package com.app.intelligent.board.utils;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.app.intelligent.board.AppContextHelper;
import com.app.intelligent.board.R;
public class AppCustomTextView extends androidx.appcompat.widget.AppCompatTextView {
public AppCustomTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initAttrs(context,attrs);
}
private void initAttrs(Context context, AttributeSet attrs){
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyTV);
boolean aBoolean = typedArray.getBoolean(R.styleable.MyTV_apply_font,false);
if(aBoolean){
initFont(this);
}
typedArray.recycle();
}
public static void initFont(TextView tv) {
tv.setTypeface(AppContextHelper.Companion.getDefaultFont());
}
}

View File

@ -0,0 +1,106 @@
package com.app.intelligent.board.utils;
import com.app.intelligent.board.uikeyboard.KeyboardKeyItemModel;
import com.app.intelligent.board.uikeyboard.KeyboardActionResponseConfig;
import com.app.intelligent.board.uikeyboard.KeyboardResponseAreaLayout;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
public class KeyboardConfigFileHandler {
public static KeyboardActionResponseConfig initConfig(String path) {
String filePath = "keyboard.conf"; // 文件路径
KeyboardActionResponseConfig config = parseConfig(path);
return config;
}
public static KeyboardActionResponseConfig parseConfig(String filePath) {
// InputStream open = App.appInstance.getAssets().open(filePath);
KeyboardActionResponseConfig config = new KeyboardActionResponseConfig();
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
KeyboardResponseAreaLayout currentLayout = null;
while ((line = br.readLine()) != null) {
line = line.trim();
if (line.isEmpty()) {
continue; // 跳过空行
}
if (line.startsWith("Version:")) {
config.setVersion(line.split(":")[1].trim());
} else if (line.startsWith("SupportLayouts:")) {
config.setSupportLayouts(line.split(":")[1].trim());
} else if (line.startsWith("HideHint:")) {
config.setHideHint(Integer.parseInt(line.split(":")[1].trim()));
} else if (line.startsWith("LayoutStyle:")) {
config.setLayoutStyle(line.split(":")[1].trim());
} else if (line.equals("KeyDefault") || line.equals("KeyMarkDefault") || line.equals("KeyFuncDefault")) {
LinkedHashMap<String, String> maps = config.getMaps();
maps.put(line, "");
} else if (line.contains(":") && currentLayout == null) {
String[] parts = line.split(":");
String keyName = parts[0].trim();
String keyValue = parts[1].trim();
String latestKey = null;
LinkedHashMap<String, String> maps = config.getMaps();
for (Map.Entry<String, String> entry : maps.entrySet()) {
latestKey = entry.getKey();
}
if (latestKey != null) {
maps.put(latestKey, keyValue);
}
} else if (line.startsWith("Row")) {
currentLayout = new KeyboardResponseAreaLayout(line.split(":")[0].trim());
config.addLayout(currentLayout);
} else if (currentLayout != null) {
if (line.equals("Key")) {
String[] parts = line.split(":");
String keyName = parts[0].trim();
KeyboardKeyItemModel keyModel = new KeyboardKeyItemModel(keyName);
currentLayout.addKey(keyModel);
} else if (line.contains(":") && currentLayout.getLastKey().getBackground() == null) {
// 解析按键的其他属性 Label
String[] parts = line.split(":");
String keyName = parts[0].trim();
String keyValue = parts[1].trim();
KeyboardKeyItemModel keyModel = currentLayout.getLastKey();
if (keyName.equals("Label")) {
keyModel.setLabel(keyValue);
}
if (keyName.equals("Background")) {
keyModel.setBackground(keyValue);
}
} else {
if (line.equals("KeyShift") || line.equals("KeyDelete") || line.equals("KeyAlphaSymbol") || line.equals("KeyEmoji")
|| line.equals("KeyMark")
|| line.equals("KeySpace")
|| line.equals("KeyEnter")) {
KeyboardKeyItemModel funcationKeyModel = new KeyboardKeyItemModel(line);
config.addKey(funcationKeyModel);
} else if (line.contains(":")) {
String[] parts = line.split(":");
String keyName = parts[0].trim();
String keyValue = parts[1].trim();
KeyboardKeyItemModel lastKeyModel = config.getLastKeyList();
if (keyName.equals("Label")) {
lastKeyModel.setLabel(keyValue);
}
if (keyName.equals("Background")) {
lastKeyModel.setBackground(keyValue);
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return config;
}
}

View File

@ -0,0 +1,60 @@
package com.app.intelligent.board.utils
object KeyboardKeyConst {
const val KEY_CODE_DELETE = -5
//同一个按键
const val KEY_CODE_SHIFT = -1
const val KEY_CODE_NUMBER_SHIFT = -103
const val KEY_CODE_SYMBOL_SHIFT = -101
//同一个按键
const val KEY_CODE_CHANGE_NUMBER = -2
const val KEY_CODE_BACK = -102
const val KEY_CODE_COMPLETE = -4
const val KEY_CODE_CANCEL = -3
const val KEY_CODE_SPACE = 32
const val functionNormalName = "btn_keyboard_key_functional_normal.9.png"
const val functionPressName = "btn_keyboard_key_functional_pressed.9.png"
const val normalName = "btn_keyboard_key_normal_normal.9.png"
const val pressName = "btn_keyboard_key_normal_pressed.9.png"
const val toNormalName="btn_keyboard_key_toggle_normal_on.9.png"
const val toPressName="btn_keyboard_key_toggle_pressed_on.9.png"
const val spaceNormalName = "btn_keyboard_spacekey_normal_normal.9.png"
const val spacePressName = "btn_keyboard_spacekey_normal_pressed.9.png"
const val imeSwitchName ="ic_ime_switcher.png"
const val deleteNormalName = "sym_keyboard_delete_normal.png"
const val deletePressName = "sym_keyboard_delete_pressed.png"
const val backName ="sym_keyboard_return_normal.png"
const val searchName ="sym_keyboard_search.png"
const val shiftNormalName ="sym_keyboard_shift.png"
const val shiftLockName ="sym_keyboard_shift_locked.png"
const val keyTextColorName ="key_text_color_normal"
const val keyTextColorFunctionName ="key_text_color_functional"
const val videoName ="keyboard_background_video.mp4"
const val bgName ="keyboard_background.jpg"
const val bgName_png ="keyboard_background.png"
const val previewBg="keyboard_preview_screenshot.jpg"
const val video ="keyboard_background_video.gif"
}

View File

@ -0,0 +1,76 @@
package com.app.intelligent.board.utils;
import android.graphics.Rect;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import com.app.intelligent.board.AppContextHelper;
public class KeyboardRvItemDecoration extends RecyclerView.ItemDecoration {
private int v, h, ex;
public KeyboardRvItemDecoration(int v, int h, int ex) {
this.v = Math.round(dpToPx(v));
this.h = Math.round(dpToPx(h));
this.ex = Math.round(dpToPx(ex));
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int spanCount = 1;
int spanSize = 1;
int spanIndex = 0;
int childAdapterPosition = parent.getChildAdapterPosition(view);
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
spanCount = staggeredGridLayoutManager.getSpanCount();
if (layoutParams.isFullSpan()) {
spanSize = spanCount;
}
spanIndex = layoutParams.getSpanIndex();
} else if (layoutManager instanceof GridLayoutManager) {
GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
GridLayoutManager.LayoutParams layoutParams = (GridLayoutManager.LayoutParams) view.getLayoutParams();
spanCount = gridLayoutManager.getSpanCount();
spanSize = gridLayoutManager.getSpanSizeLookup().getSpanSize(childAdapterPosition);
spanIndex = layoutParams.getSpanIndex();
} else if (layoutManager instanceof LinearLayoutManager) {
outRect.left = v;
outRect.right = v;
outRect.bottom = h;
}
if (spanSize == spanCount) {
outRect.left = v + ex;
outRect.right = v + ex;
outRect.bottom = h;
} else {
int itemAllSpacing = (v * (spanCount + 1) + ex * 2) / spanCount;
int left = v * (spanIndex + 1) - itemAllSpacing * spanIndex + ex;
int right = itemAllSpacing - left;
outRect.left = left;
outRect.right = right;
outRect.bottom = h;
}
}
public static float dpToPx(float dpValue) {
float density = AppContextHelper.appInstance.getResources().getDisplayMetrics().density;
return density * dpValue + 0.5f;
}
}

View File

@ -0,0 +1,181 @@
package com.app.intelligent.board.utils
import android.content.Context
import android.graphics.BitmapFactory
import android.graphics.Color
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.StateListDrawable
import android.util.Xml
import androidx.core.content.ContextCompat
import com.app.intelligent.board.AppContextHelper
import com.app.intelligent.board.uikeyboard.KeyboardActionResponseConfig
import com.app.intelligent.board.R
import org.xmlpull.v1.XmlPullParser
import java.io.File
import java.io.StringReader
import kotlin.collections.iterator
class KeyboardThemeManager(var context: Context) {
private var textSize = 13f
var functionDraw: Drawable =
getDefaultDrawList(R.drawable.default_normal_key, R.drawable.default_pressed_key)
var generalDraw: Drawable =
getDefaultDrawList(R.drawable.default_normal_key, R.drawable.default_pressed_key)
var toDraw: Drawable = getDefaultDrawList(R.drawable.default_normal_key, R.drawable.default_pressed_key)
var spaceDraw: Drawable = getDefaultDrawList(R.drawable.default_normal_key, R.drawable.default_pressed_key)
var switchDraw: Drawable? = null
var deleteDraw: Drawable? = null
var backDraw: Drawable? = null
var searchDraw: Drawable? = null
var shiftDraw: Drawable? = null
var shiftLockDraw: Drawable? = null
var keyTextColor: Int = ContextCompat.getColor(context, R.color.black)
var keyTextColorFunction: Int = ContextCompat.getColor(context, R.color.black)
fun getConfig(): KeyboardActionResponseConfig? {
val skinPath = KeyboardThemeSaver.getSkinPath()
val configFilePath = skinPath + "assets/keyboard.conf"
val file = File(configFilePath)
return if (file.exists()) {
KeyboardConfigFileHandler.initConfig(configFilePath)
} else {
null
}
}
fun getConfigBg(name: String): Drawable? {
KeyboardThemeSaver.getSkinPath()?.let { resPath ->
val pPath = "${resPath}res/drawable-xhdpi-v4/"
return getDrawList(
pPath + name,
pPath + name
)
}
return null
}
fun updateSkinConfig() {
KeyboardThemeSaver.getSkinPath()?.let { resPath ->
val pPath = "${resPath}res/drawable-xhdpi-v4/"
pPath.let {
readColors(resPath) {
for ((name, value) in it) {
if (name == KeyboardKeyConst.keyTextColorName) {
keyTextColor = value
}
if (name == KeyboardKeyConst.keyTextColorFunctionName) {
keyTextColorFunction = value
}
}
}
functionDraw = getDrawList(
it + KeyboardKeyConst.functionNormalName,
it + KeyboardKeyConst.functionPressName
)
generalDraw = getDrawList(it + KeyboardKeyConst.normalName, it + KeyboardKeyConst.pressName)
toDraw = getDrawList(it + KeyboardKeyConst.toNormalName, it + KeyboardKeyConst.toPressName)
spaceDraw =
getDrawList(it + KeyboardKeyConst.spaceNormalName, it + KeyboardKeyConst.spacePressName)
switchDraw =
getDrawList(it + KeyboardKeyConst.imeSwitchName, it + KeyboardKeyConst.imeSwitchName)
deleteDraw = getDrawList(
it + KeyboardKeyConst.deleteNormalName,
it + KeyboardKeyConst.deletePressName
)
backDraw = getDrawList(it + KeyboardKeyConst.backName, it + KeyboardKeyConst.backName)
searchDraw = getDrawList(it + KeyboardKeyConst.searchName, it + KeyboardKeyConst.searchName)
shiftDraw = getDrawList(
it + KeyboardKeyConst.shiftNormalName,
it + KeyboardKeyConst.shiftNormalName
)
shiftLockDraw =
getDrawList(it + KeyboardKeyConst.shiftLockName, it + KeyboardKeyConst.shiftLockName)
}
}
}
private fun getDefaultDrawList(normalDrawId: Int, pressDrawId: Int): StateListDrawable {
val normalDraw = ContextCompat.getDrawable(AppContextHelper.Companion.appInstance, normalDrawId)
val pressDraw = ContextCompat.getDrawable(AppContextHelper.Companion.appInstance, pressDrawId)
val stateListDrawable = StateListDrawable().apply {
addState(
intArrayOf(android.R.attr.state_pressed),
pressDraw
)
addState(intArrayOf(), normalDraw)
}
return stateListDrawable
}
private fun getDrawList(normalPath: String, pressPath: String): StateListDrawable {
val pressDraw = BitmapFactory.decodeFile(pressPath)
val normalDraw = BitmapFactory.decodeFile(normalPath)
val stateListDrawable = StateListDrawable().apply {
addState(
intArrayOf(android.R.attr.state_pressed),
BitmapDrawable(context.resources, pressDraw)
)
addState(intArrayOf(), BitmapDrawable(context.resources, normalDraw))
}
return stateListDrawable
}
private fun readColors(resPath: String, callBack: (Map<String, Int>) -> Unit) {
val resMaps = mutableMapOf<String, Int>()
val pPath = "${resPath}res/colors.xml"
val file = File(pPath)
if (file.exists()) {
val xmlPullParser = Xml.newPullParser().apply {
setInput(StringReader(file.readText()))
setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
}
var curType = xmlPullParser.eventType
while (curType != XmlPullParser.END_DOCUMENT) {
val b = curType == XmlPullParser.START_TAG
val b1 = xmlPullParser.name == "color"
val b2 = xmlPullParser.name == "item"
if (b && (b1 || b2)) {
val attributeName = xmlPullParser.getAttributeValue(null, "name")
val nextTextValue = xmlPullParser.nextText()
val b3 = attributeName == KeyboardKeyConst.keyTextColorName
val b4 = attributeName == KeyboardKeyConst.keyTextColorFunctionName
if (b3 || b4) {
resMaps[attributeName] = Color.parseColor(nextTextValue)
}
}
curType = xmlPullParser.next()
}
}
callBack.invoke(resMaps)
}
}

View File

@ -0,0 +1,19 @@
package com.app.intelligent.board.utils
import android.content.Context
import com.app.intelligent.board.AppContextHelper
object KeyboardThemeSaver {
val SP_NAME = "keyboard_skin"
val SKIN_PATH = "skin_path"
val spSkin = AppContextHelper.Companion.appInstance.getSharedPreferences(SP_NAME,Context.MODE_PRIVATE)
fun updateSkinPath(skinPath:String){
spSkin.edit().putString(SKIN_PATH,skinPath).apply()
}
fun getSkinPath( ):String?{
return spSkin.getString(SKIN_PATH,null)
}
}

View File

@ -0,0 +1,238 @@
package com.app.intelligent.board.utils;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import com.app.intelligent.board.AppContextHelper;
import com.app.intelligent.board.uilistener.KeyboardConfigChangeListener;
import net.sf.sevenzipjbinding.ArchiveFormat;
import net.sf.sevenzipjbinding.IArchiveOpenCallback;
import net.sf.sevenzipjbinding.IInArchive;
import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.SevenZipException;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.impl.RandomAccessFileOutStream;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class KeyboardZipFileHandler {
public static void startDownloadZip(String zipPath, KeyboardConfigChangeListener callback) {
OkHttpClient clientZip = new OkHttpClient().newBuilder().
connectTimeout(20, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS).build();
Request.Builder builder = new Request.Builder();
Request request = builder.get().url(zipPath).build();
clientZip.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
callback.OnApplySkinListener(null);
}
@Override
public void onResponse(Call call, Response response) {
InputStream inputStream = response.body().byteStream();
long l = response.body().contentLength();
MediaType mediaType = response.body().contentType();
saveZipFile(inputStream, getServiceZipName(zipPath), callback);
}
});
}
public static String getServiceZipName(String zipPath) {
String pointStr = "/";
int lastIndexOf = zipPath.lastIndexOf(pointStr);
String zipName = zipPath.substring(lastIndexOf + pointStr.length());
return zipName;
}
private static String getunZipFolderName(String zipPath) {
String pointStr = ".";
int lastIndexOf = zipPath.lastIndexOf(pointStr);
String zipName = zipPath.substring(0, lastIndexOf);
return zipName;
}
private static void saveZipFile(InputStream inputStream, String zipFileName, KeyboardConfigChangeListener callback) {
File zipfFile = new File(AppContextHelper.appInstance.getFilesDir(), zipFileName);
Log.d("-------------------","-------zipFileName="+zipFileName);
byte[] bytes = new byte[4096];
int readLength = 0;
InputStream is = inputStream;
FileOutputStream fileOs = null;
try {
fileOs = new FileOutputStream(zipfFile);
while ((readLength = is.read(bytes)) != -1) {
fileOs.write(bytes, 0, readLength);
}
fileOs.flush();
} catch (Exception exception) {
} finally {
try {
if (is != null) {
is.close();
}
if (fileOs != null) {
fileOs.close();
}
} catch (IOException ioException) {
}
un7ZZipFile(zipfFile, callback);
}
}
public static String getUnzipPath(String zipName){
String folderName = getunZipFolderName(zipName);
String replace = folderName.replace(".", "");
return AppContextHelper.appInstance.getFilesDir().getPath() + "/" + replace;
}
private static void un7ZZipFile(File saveZipFile, KeyboardConfigChangeListener callback) {
List<File> fileList = new ArrayList<>();
String unzipFolderPath = getUnzipPath(saveZipFile.getName());
try {
RandomAccessFileInStream inStream = new RandomAccessFileInStream(new RandomAccessFile(saveZipFile, "r"));
IInArchive open = SevenZip.openInArchive(ArchiveFormat.SEVEN_ZIP, inStream, new IArchiveOpenCallback() {
@Override
public void setTotal(Long files, Long bytes) {
}
@Override
public void setCompleted(Long files, Long bytes) {
}
});
ISimpleInArchive simple = open.getSimpleInterface();
for (ISimpleInArchiveItem archiveItem : simple.getArchiveItems()) {
RandomAccessFileOutStream outStream = null;
try {
File itemFile;
if (archiveItem.isFolder()) {
File itemFolder = new File(unzipFolderPath, archiveItem.getPath());
boolean mkdirs = itemFolder.mkdirs();
continue;
} else {
itemFile = new File(unzipFolderPath, archiveItem.getPath());
if (!itemFile.getParentFile().exists()) {
boolean mkdirs = itemFile.getParentFile().mkdirs();
}
}
outStream = new RandomAccessFileOutStream(new RandomAccessFile(itemFile, "rw"));
archiveItem.extractSlow(outStream);
fileList.add(itemFile);
} finally {
if (outStream != null) {
outStream.close();
}
}
}
inStream.close();
open.close();
} catch (FileNotFoundException | SevenZipException exception) {
} catch (IOException ioException) {
} finally {
if (saveZipFile.exists()) {
saveZipFile.delete();
}
callback.OnApplySkinListener(fileList);
}
}
public static File findFirstDirectory(File dir) {
if (dir.isDirectory()) {
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
return file; // 返回第一个文件目录
}
}
}
}
return null; // 如果没有找到文件目录则返回null
}
private static Bitmap drawableToBitmap(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
// 如果不是 BitmapDrawable则创建一个空的 Bitmap 对象
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
// Bitmap 保存到文件
private static void saveBitmapToFile(Bitmap bitmap, File file) throws IOException {
if(!file.exists()){
file.createNewFile();
}
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); // 保存为 PNG 格式
out.flush();
out.close();
}
// 示例 Drawable 写入文件
public static void saveDrawableToFile(Drawable drawable, File file) throws IOException {
Bitmap bitmap = drawableToBitmap(drawable);
saveBitmapToFile(bitmap, file);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M608,736c-6.4,0 -19.2,0 -25.6,-6.4l-192,-192C384,524.8 384,499.2 390.4,486.4l192,-192c12.8,-12.8 32,-12.8 44.8,0s12.8,32 0,44.8L460.8,512l166.4,166.4c12.8,12.8 12.8,32 0,44.8C627.2,736 614.4,736 608,736z"
android:fillColor="@color/main_text_color"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/main_color_load" />
<corners android:radius="12dp" />
</shape>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/apply_step_false" />
<corners android:radius="12dp" />
</shape>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white"/>
<corners
android:topLeftRadius="40dp"
android:topRightRadius="40dp" />
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="10dp" />
<solid android:color="@color/white" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="10dp" />
<solid android:color="@color/main_color_load"/>
</shape>
</clip>
</item>
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 选中状态(已收藏):替换为你项目中“已收藏”的图标 -->
<item android:drawable="@drawable/fav_1" android:state_selected="true"/>
<!-- 未选中状态(未收藏):替换为你项目中“未收藏”的图标 -->
<item android:drawable="@drawable/fav_2" android:state_selected="false"/>
<!-- 默认状态 -->
<item android:drawable="@drawable/fav_1"/>
</selector>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false">
<shape android:shape="rectangle">
<corners android:radius="4dp"/>
<solid android:color="@color/white"/>
</shape>
</item>
<item android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:radius="4dp"/>
<solid android:color="@color/key_bg_press"/>
</shape>
</item>
</selector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_btn_normal" android:state_selected="false" />
<item android:drawable="@drawable/bg_dialog_enable" />
</selector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/white"/>
</shape>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white"/>
<corners android:radius="16dp"/>
<stroke
android:width="1dp"
android:color="@color/color_gray"/>
</shape>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- 容器背景色 -->
<solid android:color="@color/white" />
<!-- 容器圆角 -->
<corners android:radius="8dp" />
<!-- 容器边框 -->
<stroke
android:width="1dp"
android:color="@color/color_gray" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/gray"/>
<corners android:radius="4dp"/>
</shape>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/white" />
<corners android:radius="16dp" />
</shape>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/white" />
<corners android:radius="16dp" />
<!-- 适中的内边距16dp保证圆角矩形宽度美观 -->
<padding
android:bottom="4dp"
android:left="16dp"
android:right="16dp"
android:top="4dp" />
</shape>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#F8C8DC"/>
<corners android:radius="16dp"/> <!-- 圆角大小可调整 -->
</shape>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:pathData="M8.72,8L14.21,2.51C14.412,2.307 14.412,1.992 14.21,1.79C14.007,1.587 13.692,1.587 13.49,1.79L8,7.28L2.51,1.79C2.307,1.587 1.992,1.587 1.79,1.79C1.587,1.992 1.587,2.307 1.79,2.51L7.28,8L1.79,13.49C1.587,13.692 1.587,14.007 1.79,14.21C1.88,14.3 2.015,14.367 2.15,14.367C2.285,14.367 2.42,14.322 2.51,14.21L8,8.72L13.49,14.21C13.58,14.3 13.715,14.367 13.85,14.367C13.985,14.367 14.12,14.322 14.21,14.21C14.412,14.007 14.412,13.692 14.21,13.49L8.72,8Z"
android:fillColor="#000000"/>
</vector>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="10dp"/>
<solid android:color="@color/white"/>
</shape>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 进度条背景 -->
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<solid android:color="#EEEEEE" />
<corners android:radius="3dp" />
</shape>
</item>
<!-- 进度条已完成部分 -->
<item android:id="@android:id/progress">
<clip>
<shape android:shape="rectangle">
<solid android:color="@color/main_text_color" />
<corners android:radius="3dp" />
</shape>
</clip>
</item>
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- 白色背景 -->
<solid android:color="@android:color/white"/>
<!-- 圆角(可调整数值) -->
<corners android:radius="16dp"/>
</shape>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/main"
android:background="@color/main_color_bg"
tools:context="com.app.intelligent.board.uiactivity.CategoryItemListActivity">
<ImageView
android:id="@+id/back"
android:layout_width="44dp"
android:layout_height="44dp"
android:layout_marginTop="5dp"
android:paddingVertical="7dp"
android:paddingStart="5dp"
android:src="@drawable/back_back"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.app.intelligent.board.utils.AppCustomTextView
android:id="@+id/class_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textColor="@color/main_text_color"
android:textSize="18sp"
app:apply_font="true"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="@id/back"
app:layout_constraintTop_toTopOf="@id/back"
app:layout_constraintLeft_toRightOf="@id/back"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintHorizontal_bias="0.0" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="0dp"
android:paddingStart="10dp"
android:paddingEnd="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/back" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:id="@+id/main"
android:background="@color/main_color_bg"
android:orientation="vertical">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/tab_layout" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_alignParentBottom="true"
android:background="@color/white"
app:tabIndicatorHeight="0dp"
app:elevation="0dp"
app:tabRippleColor="@null"
app:tabBackground="@null"
app:tabPaddingStart="0dp"
app:tabPaddingEnd="0dp"
app:tabPaddingTop="0dp"
app:tabPaddingBottom="0dp"/>
</RelativeLayout>

View File

@ -0,0 +1,192 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/main"
android:background="@color/main_color_bg">
<ImageView
android:id="@+id/back"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="5dp"
android:paddingStart="5dp"
android:paddingVertical="7dp"
android:src="@drawable/back"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:tint="@color/dark_gray"/>
<com.app.intelligent.board.utils.AppCustomTextView
android:id="@+id/textview_data_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textColor="@color/dark_gray"
android:textSize="18sp"
app:apply_font="true"
app:layout_constraintBottom_toBottomOf="@id/back"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/back" />
<androidx.cardview.widget.CardView
android:id="@+id/card_viewData"
android:layout_width="match_parent"
android:layout_height="204dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
app:cardBackgroundColor="@color/transparent"
app:cardCornerRadius="12dp"
app:cardElevation="0dp"
app:layout_constraintTop_toBottomOf="@id/back">
<ImageView
android:id="@+id/image_data"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY" />
</androidx.cardview.widget.CardView>
<LinearLayout
android:id="@+id/linearLayout_container"
android:layout_width="260dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="28dp"
android:layout_marginEnd="16dp"
android:gravity="center"
android:orientation="vertical"
android:layout_marginBottom="10dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/card_viewData">
<LinearLayout
android:id="@+id/layoutFavoriteApply"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@drawable/bg_btn_normal"
android:gravity="center"
android:orientation="horizontal"
android:layout_marginBottom="10dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground">
<ImageView
android:id="@+id/im_like"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/selector_fav_state"
android:clickable="false"
android:focusable="false"/>
<com.app.intelligent.board.utils.AppCustomTextView
android:id="@+id/tv_favorite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@string/favorite_apply"
app:apply_font="true"
android:textColor="@color/white"
android:textSize="16sp"
android:clickable="false"
android:focusable="false" />
</LinearLayout>
<LinearLayout
android:id="@+id/layoutDownloadApply"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@drawable/bg_btn_normal"
android:gravity="center"
android:orientation="horizontal"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground">
<ImageView
android:id="@+id/im_download"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/download3"
android:clickable="false"
android:focusable="false" />
<com.app.intelligent.board.utils.AppCustomTextView
android:id="@+id/tv_download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@string/download_apply"
app:apply_font="true"
android:textColor="@color/white"
android:textSize="16sp"
android:clickable="false"
android:focusable="false" />
</LinearLayout>
</LinearLayout>
<com.app.intelligent.board.utils.AppCustomTextView
android:id="@+id/text_for_you"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="25dp"
android:text="@string/recommended"
android:textColor="@color/dark_gray"
android:textSize="16sp"
app:apply_font="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/linearLayout_container" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/recommended_viewpager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="8dp"
android:minHeight="200dp"
app:layout_constraintBottom_toTopOf="@id/progress_indicator"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_for_you" />
<ProgressBar
android:id="@+id/progress_indicator"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="120dp"
android:layout_height="6dp"
android:layout_marginBottom="16dp"
android:progressDrawable="@drawable/style_progress_loading"
android:max="100"
android:progress="0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<FrameLayout
android:id="@+id/loading"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_loading"
android:clickable="true"
android:focusable="true"
android:visibility="gone">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminateTint="@color/white" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:id="@+id/main"
android:layout_height="wrap_content">
<RelativeLayout
android:id="@+id/relayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_loading" />
<EditText
android:id="@+id/et"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_marginStart="5dp"
android:layout_marginTop="50dp"
android:layout_marginEnd="5dp"
android:background="@drawable/style_edit_success"
android:focusable="true"
android:focusableInTouchMode="true"
android:hint="@string/et_hint"
android:paddingStart="15dp"
android:textColor="@color/black"
android:textColorHint="@color/apply_step_false"
android:textCursorDrawable="@color/color_74CBFF" />
</RelativeLayout>
<ImageView
android:id="@+id/id_back"
android:layout_width="44dp"
android:layout_height="44dp"
android:layout_marginTop="5dp"
android:paddingStart="5dp"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:src="@drawable/back_white" />
<com.app.intelligent.board.utils.AppCustomTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/id_back"
android:layout_alignBottom="@id/id_back"
android:layout_centerHorizontal="true"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="18sp"
app:apply_font="true" />
</RelativeLayout>

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/main_color_bg"
android:padding="8dp">
<!-- 搜索栏 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_marginBottom="8dp"> <!-- 底部边距保持不变 -->
<!-- 返回按钮 -->
<ImageView
android:id="@+id/iv_back"
android:layout_width="36dp"
android:layout_height="36dp"
android:src="@drawable/back"
android:tint="@color/dark_gray"
android:clickable="true"
android:focusable="true"
android:scaleType="centerInside"
android:padding="8dp"
/>
<EditText
android:id="@+id/et_search"
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_weight="1"
android:hint="@string/search_content"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:background="@drawable/shape_search_input"
android:textSize="14sp"
android:singleLine="true"
android:imeOptions="actionSearch"/>
<!-- 搜索按钮 -->
<ImageView
android:id="@+id/iv_search_btn"
android:layout_width="36dp"
android:layout_height="36dp"
android:src="@drawable/search1"
android:tint="@color/dark_gray"
android:scaleType="centerInside"
android:padding="8dp"
android:background="@android:color/transparent"
android:clickable="true"
android:focusable="true"
/>
</LinearLayout>
<!-- 内容容器 -->
<FrameLayout
android:id="@+id/fl_content"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

Some files were not shown because too many files have changed in this diff Show More