登录功能

68 阅读8分钟

📁 项目目录结构

app/src/main/java/com/example/app/
├── model/
│   ├── LoginRequest.java          # 登录请求数据模型
│   └── LoginResponse.java         # 登录响应数据模型
├── api/
│   └── LoginApiService.java       # API接口定义
├── network/
│   └── ApiClient.java             # 网络客户端管理
└── activity/
    └── LoginActivity.java         # 登录Activity

🔵 标准做法

  1. MVVM架构 - 继承BaseMvvmActivity,使用ViewBinding
  2. Retrofit网络框架 - Android主流网络请求库
  3. 单例模式 - ApiClient管理Retrofit实例
  4. 异步回调 - 使用Callback处理网络响应
  5. TextWatcher - 监听输入框变化进行实时验证
  6. Intent传递数据 - Activity间标准数据传递方式
  7. Toast提示 - 标准的用户反馈方式
  8. 输入验证 - 使用Android内置Patterns验证邮箱
  9. 生命周期管理 - 在适当的生命周期方法中初始化

🟡 项目特定优化

  1. @Data注解 - 使用Lombok减少getter/setter样板代码
  2. TP-Link API参数 - 所有带注释"项目特定"的API参数配置
  3. 简化登录流程 - 去掉缓存、自动登录等复杂功能
  4. 自定义验证规则 - 用户名≥3位,密码≥6位的业务规则
  5. 设备信息硬编码 - terminalName、platform等设备参数
dependencies {
    // Lombok
    compileOnly 'org.projectlombok:lombok:1.18.30'
    annotationProcessor 'org.projectlombok:lombok:1.18.30'
    // Retrofit for network requests
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    
    // OkHttp for HTTP client
    implementation 'com.squareup.okhttp3:logging-interceptor:4.10.0'
    
    // Gson for JSON parsing
    implementation 'com.google.code.gson:gson:2.10.1'
}
*/

// 1. 登录请求数据模型
import lombok.Data;

@Data
public class LoginRequest {
    private String appType;
    private String appVersion;
    private String cloudPassword;
    private String cloudUserName;
    private String platform;
    private boolean refreshTokenNeeded;
    private String terminalMeta;
    private String terminalName;
    private String terminalUUID;

    public LoginRequest(String username, String password) {
        this.appType = "TP-Link_aria_Android";
        this.appVersion = "3.8.33";
        this.cloudUserName = username;
        this.cloudPassword = password;
        this.platform = "Android 12";
        this.refreshTokenNeeded = false;
        this.terminalMeta = "1";
        this.terminalName = "Redmi Note 9 Pro";
        this.terminalUUID = generateTerminalUUID();
    }

    private String generateTerminalUUID() {
        return java.util.UUID.randomUUID().toString().replace("-", "").toUpperCase();
    }
}

// 2. 登录响应数据模型
import lombok.Data;

@Data
public class LoginResponse {
    private int error_code;
    private LoginResult result;

    @Data
    public static class LoginResult {
        private String accountId;
        private String regionCode;
        private String regTime;
        private String avatarUrl;
        private String appServerUrl;
        private int riskDetected;
        private String nickname;
        private String avatarHttpsUrl;
        private String errorCode;
        private String email;
        private String token;
    }
}

// 3. 网络服务接口
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;
import retrofit2.http.Query;

public interface LoginApiService {
    @POST("/api/v2/account/captchaLogin")
    Call<LoginResponse> login(
        @Query("appName") String appName,
        @Query("appVer") String appVer,
        @Query("netType") String netType,
        @Query("termID") String termID,
        @Query("ospf") String ospf,
        @Query("brand") String brand,
        @Query("locale") String locale,
        @Query("model") String model,
        @Query("termName") String termName,
        @Query("termMeta") String termMeta,
        @Body LoginRequest loginRequest
    );
}

// 4. 网络客户端管理器
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import java.util.concurrent.TimeUnit;

public class ApiClient {
    private static final String BASE_URL = "https://n-wap-beta.tplinkcloud.com";
    private static Retrofit retrofit;

    public static Retrofit getRetrofitInstance() {
        if (retrofit == null) {
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);

            OkHttpClient client = new OkHttpClient.Builder()
                    .addInterceptor(logging)
                    .connectTimeout(30, TimeUnit.SECONDS)
                    .readTimeout(30, TimeUnit.SECONDS)
                    .writeTimeout(30, TimeUnit.SECONDS)
                    .build();

            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .client(client)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }

    public static LoginApiService getLoginService() {
        return getRetrofitInstance().create(LoginApiService.class);
    }
}

// 5. 简化的LoginActivity (去掉缓存和自动登录)
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.example.app.databinding.ActivityLoginBinding;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class LoginActivity extends BaseMvvmActivity {
    
    private LoginApiService loginApiService;

    @Nullable
    @Override
    protected ActivityLoginBinding bindContentView(@Nullable Bundle bundle) {
        return ActivityLoginBinding.inflate(getLayoutInflater());
    }

    @Override
    protected void subscribeViewModel(@Nullable Bundle bundle) {
        // 初始化网络服务
        loginApiService = ApiClient.getLoginService();
        
        setupViews();
    }

    private void setupViews() {
        // Initialize login button as disabled
        getViewBinding().loginButton.setEnabled(false);

        // Add text watchers for input validation
        TextWatcher textWatcher = new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {}

            @Override
            public void afterTextChanged(Editable s) {
                validateInputs();
            }
        };

        // Attach text watchers to input fields
        getViewBinding().usernameEditText.addTextChangedListener(textWatcher);
        getViewBinding().passwordEditText.addTextChangedListener(textWatcher);

        // Set click listeners
        getViewBinding().forgotPasswordButton.setOnClickListener(v -> {
            startActivity(new Intent(LoginActivity.this, ForgotPasswordActivity.class));
        });

        getViewBinding().registerButton.setOnClickListener(v -> {
            startActivity(new Intent(LoginActivity.this, RegisterActivity.class));
        });

        getViewBinding().loginButton.setOnClickListener(v -> performLogin());
    }

    private void validateInputs() {
        String username = getViewBinding().usernameEditText.getText().toString().trim();
        String password = getViewBinding().passwordEditText.getText().toString().trim();
        
        boolean isValid = !TextUtils.isEmpty(username) && !TextUtils.isEmpty(password) && 
                         username.length() >= 3 && password.length() >= 6;
        
        getViewBinding().loginButton.setEnabled(isValid);
    }

    private void performLogin() {
        String username = getViewBinding().usernameEditText.getText().toString().trim();
        String password = getViewBinding().passwordEditText.getText().toString().trim();

        // 输入验证
        if (TextUtils.isEmpty(username)) {
            showError("请输入用户名");
            return;
        }

        if (TextUtils.isEmpty(password)) {
            showError("请输入密码");
            return;
        }

        if (!android.util.Patterns.EMAIL_ADDRESS.matcher(username).matches()) {
            showError("请输入有效的邮箱地址");
            return;
        }

        // 显示加载状态
        setLoadingState(true);

        // 创建登录请求
        LoginRequest loginRequest = new LoginRequest(username, password);

        // 发起网络请求
        Call<LoginResponse> call = loginApiService.login(
                "TP-Link_aria_Android",
                "3.8.33",
                "wifi",
                loginRequest.getTerminalUUID(),
                "Android 12",
                "TPLINK",
                "en_US",
                "Redmi Note 9 Pro",
                "Redmi Note 9 Pro",
                "1",
                loginRequest
        );

        call.enqueue(new Callback<LoginResponse>() {
            @Override
            public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
                setLoadingState(false);
                
                if (response.isSuccessful() && response.body() != null) {
                    handleLoginSuccess(response.body());
                } else {
                    handleLoginError("登录失败,请检查网络连接");
                }
            }

            @Override
            public void onFailure(Call<LoginResponse> call, Throwable t) {
                setLoadingState(false);
                handleLoginError("网络错误: " + t.getMessage());
            }
        });
    }

    private void handleLoginSuccess(LoginResponse response) {
        if (response.getError_code() == 0 && response.getResult() != null) {
            // 登录成功,可以在这里处理用户数据
            LoginResponse.LoginResult result = response.getResult();
            
            // 显示成功消息
            Toast.makeText(this, "登录成功,欢迎 " + result.getNickname(), Toast.LENGTH_SHORT).show();
            
            // 跳转到主界面,传递必要的用户信息
            Intent intent = new Intent(this, MainActivity.class);
            intent.putExtra("user_token", result.getToken());
            intent.putExtra("user_id", result.getAccountId());
            intent.putExtra("user_email", result.getEmail());
            startActivity(intent);
            finish();
        } else {
            handleLoginError("登录失败,请检查用户名和密码");
        }
    }

    private void handleLoginError(String message) {
        showError(message);
        
        // 清空密码输入框
        getViewBinding().passwordEditText.setText("");
        getViewBinding().passwordEditText.requestFocus();
    }

    private void setLoadingState(boolean isLoading) {
        getViewBinding().loginButton.setEnabled(!isLoading);
        getViewBinding().usernameEditText.setEnabled(!isLoading);
        getViewBinding().passwordEditText.setEnabled(!isLoading);
        
        if (isLoading) {
            getViewBinding().loginButton.setText("登录中...");
        } else {
            getViewBinding().loginButton.setText("登录");
        }
    }

    private void showError(String message) {
        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
       }
    }
// 1. 创建RegionSelectionHandler类来处理所有地区选择逻辑
public class RegionSelectionHandler {
    
    private Context context;
    private TextView regionTextView;
    private static final String PREFS_NAME = "app_settings";
    private static final String KEY_SELECTED_REGION = "selected_region";
    private static final String DEFAULT_REGION = "中国大陆";
    
    public RegionSelectionHandler(Context context, TextView regionTextView) {
        this.context = context;
        this.regionTextView = regionTextView;
    }
    
    // 初始化地区显示
    public void initRegionDisplay() {
        updateRegionDisplay();
    }
    
    // 更新地区显示
    public void updateRegionDisplay() {
        String selectedRegion = getSelectedRegion();
        regionTextView.setText(selectedRegion);
    }
    
    // 获取保存的地区
    public String getSelectedRegion() {
        SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
        return prefs.getString(KEY_SELECTED_REGION, DEFAULT_REGION);
    }
    
    // 保存选择的地区
    public void saveSelectedRegion(String region) {
        SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
        prefs.edit().putString(KEY_SELECTED_REGION, region).apply();
        updateRegionDisplay(); // 立即更新显示
    }
    
    // 设置模态窗口内容
    public void setupModalContent(View contentView, Runnable onRegionSelected) {
        TPListViewRadioGroup checkGroup = contentView.findViewById(R.id.check_group);
        TPSearchBar tpSearch = contentView.findViewById(R.id.tp_search);
        
        // 设置当前选中的地区
        setInitialSelection(checkGroup);
        
        // 设置搜索功能
        setupSearchFunction(tpSearch, checkGroup);
        
        // 设置RadioGroup选择监听
        checkGroup.setOnCheckedChangeListener((group, checkedId) -> {
            // 可以在这里处理选择变化
        });
        
        // 保存回调,供Done按钮使用
        contentView.setTag(R.id.region_callback, onRegionSelected);
        contentView.setTag(R.id.region_handler, this);
    }
    
    // 处理Done按钮点击
    public static void handleDoneClick(View contentView) {
        RegionSelectionHandler handler = (RegionSelectionHandler) contentView.getTag(R.id.region_handler);
        Runnable callback = (Runnable) contentView.getTag(R.id.region_callback);
        
        if (handler != null) {
            TPListViewRadioGroup checkGroup = contentView.findViewById(R.id.check_group);
            int checkedId = checkGroup.getCheckedItemId();
            
            if (checkedId != -1) {
                String selectedRegion = handler.getRegionNameById(checkedId);
                if (selectedRegion != null) {
                    handler.saveSelectedRegion(selectedRegion);
                    
                    // 显示选择成功提示
                    Toast.makeText(handler.context, "已选择地区: " + selectedRegion, Toast.LENGTH_SHORT).show();
                    
                    // 执行回调
                    if (callback != null) {
                        callback.run();
                    }
                }
            }
        }
    }
    
    private void setInitialSelection(TPListViewRadioGroup checkGroup) {
        String currentRegion = getSelectedRegion();
        Map<String, String> regionMap = getRegionMap();
        
        for (Map.Entry<String, String> entry : regionMap.entrySet()) {
            if (entry.getValue().equals(currentRegion)) {
                int viewId = context.getResources().getIdentifier(entry.getKey(), "id", context.getPackageName());
                if (viewId != 0) {
                    checkGroup.check(viewId);
                }
                break;
            }
        }
    }
    
    private void setupSearchFunction(TPSearchBar tpSearch, TPListViewRadioGroup checkGroup) {
        List<RegionItem> allRegions = getAllRegions();
        List<RegionItem> filteredRegions = new ArrayList<>(allRegions);
        
        SearchViewBaseAdapter<RegionItem> searchAdapter = new SearchViewBaseAdapter<RegionItem>(filteredRegions, 
            new SearchViewBaseAdapter.OnBindDataListener<RegionItem>() {
                @Override
                public void onBindViewHolder(RegionItem model, SearchViewBaseViewHolder viewHolder, int type, int position) {
                    if (viewHolder != null && model != null) {
                        viewHolder.setText(R.id.suggestion_text, model.displayName);
                    }
                }
                
                @Override
                public int getLayoutId(int type) {
                    return R.layout.search_suggestion_item;
                }
            });
        
        searchAdapter.setItemClickListener((adapter, position) -> {
            if (position < filteredRegions.size()) {
                RegionItem selectedItem = filteredRegions.get(position);
                selectRegion(checkGroup, selectedItem);
                tpSearch.closeSearch();
            }
        });
        
        tpSearch.setIsTextChangeListener(s -> {
            filterRegions(allRegions, filteredRegions, s);
            searchAdapter.updateData(filteredRegions);
        });
        
        tpSearch.setOnQueryTextListener(new MaterialSearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                filterRegions(allRegions, filteredRegions, query);
                if (!filteredRegions.isEmpty()) {
                    selectRegion(checkGroup, filteredRegions.get(0));
                }
                return true;
            }
            
            @Override
            public boolean onQueryTextChange(String newText) {
                return false;
            }
        });
        
        tpSearch.setAdapter(searchAdapter);
    }
    
    private void selectRegion(TPListViewRadioGroup checkGroup, RegionItem region) {
        int viewId = context.getResources().getIdentifier(region.id, "id", context.getPackageName());
        if (viewId != 0) {
            checkGroup.check(viewId);
        }
    }
    
    private void filterRegions(List<RegionItem> allRegions, List<RegionItem> filteredRegions, String query) {
        if (TextUtils.isEmpty(query)) {
            filteredRegions.clear();
            filteredRegions.addAll(allRegions);
        } else {
            filteredRegions.clear();
            String lowerQuery = query.toLowerCase();
            for (RegionItem region : allRegions) {
                if (region.displayName.toLowerCase().contains(lowerQuery) ||
                    region.name.toLowerCase().contains(lowerQuery)) {
                    filteredRegions.add(region);
                }
            }
        }
    }
    
    private String getRegionNameById(int checkedId) {
        Map<String, String> regionMap = getRegionMap();
        String resourceName = context.getResources().getResourceEntryName(checkedId);
        return regionMap.get(resourceName);
    }
    
    private Map<String, String> getRegionMap() {
        Map<String, String> regionMap = new HashMap<>();
        regionMap.put("check_item1", "中国大陆");
        regionMap.put("check_item2", "中国香港");
        regionMap.put("check_item3", "中国澳门");
        regionMap.put("check_item4", "中国台湾");
        regionMap.put("check_item5", "新加坡");
        regionMap.put("check_item6", "美国");
        regionMap.put("check_item7", "日本");
        regionMap.put("check_item8", "越南");
        regionMap.put("check_item9", "巴西");
        regionMap.put("check_item10", "法国");
        regionMap.put("check_item11", "英国");
        regionMap.put("check_item12", "德国");
        return regionMap;
    }
    
    private List<RegionItem> getAllRegions() {
        return Arrays.asList(
            new RegionItem("check_item1", "mainland_china", "中国大陆"),
            new RegionItem("check_item2", "hong_kong", "中国香港"),
            new RegionItem("check_item3", "macau", "中国澳门"),
            new RegionItem("check_item4", "taiwan", "中国台湾"),
            new RegionItem("check_item5", "singapore", "新加坡"),
            new RegionItem("check_item6", "usa", "美国"),
            new RegionItem("check_item7", "japan", "日本"),
            new RegionItem("check_item8", "vietnam", "越南"),
            new RegionItem("check_item9", "brazil", "巴西"),
            new RegionItem("check_item10", "france", "法国"),
            new RegionItem("check_item11", "uk", "英国"),
            new RegionItem("check_item12", "germany", "德国")
        );
    }
    
    // RegionItem数据类
    public static class RegionItem {
        public String id;
        public String name;
        public String displayName;
        
        public RegionItem(String id, String name, String displayName) {
            this.id = id;
            this.name = name;
            this.displayName = displayName;
        }
    }
}

// 2. 需要在res/values/ids.xml中添加这些ID(如果文件不存在就创建)
/*
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="region_callback" type="id" />
    <item name="region_handler" type="id" />
</resources>
*/

// 3. 现在你的LoginActivity就非常简洁了:
/*
public class LoginActivity extends AppCompatActivity {
    
    private RegionSelectionHandler regionHandler;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 你的其他初始化代码...
        
        // 初始化地区处理器
        regionHandler = new RegionSelectionHandler(this, viewBinding.tvRegion);
        regionHandler.initRegionDisplay();
        
        setupRegionButton();
    }
    
    @Override
    protected void onResume() {
        super.onResume();
        regionHandler.updateRegionDisplay();
    }
    
    private void setupRegionButton() {
        viewBinding.tvRegion.setOnClickListener(v -> {
            new TPModalBottomSheet.Builder()
                .setScreenType(TPModalBottomSheet.ScreenType.FULL_SCREEN)
                .setDividerEnable(false)
                .setTitle("Region")
                .setEndOptionText("Done")
                .setContentLayoutId(R.layout.fragment_region)
                .setContentViewListener((tpModalBottomSheet, view) -> {
                    // 这里只需要一行代码!
                    regionHandler.setupModalContent(view, () -> {
                        // 可选:地区选择完成后的回调
                        Log.d("Region", "Region selection completed");
                    });
                })
                .setEndOptionClickListener((tpModalBottomSheet, view) -> {
                    // 这里也只需要一行代码!
                    RegionSelectionHandler.handleDoneClick(view);
                    tpModalBottomSheet.dismiss();
                    return true;
                })
                .show(getSupportFragmentManager(), "region_modal");
        });
    }
}
*/
public class RegisterActivity extends BaseMvvmActivity<ActivityRegisterBinding> {
    
    private ArrayList<String> countryList;
    private SearchViewBaseAdapter<String> searchAdapter;
    private TPModalBottomSheet currentModal;
    private ArrayList<TPSingleLineItemView> dynamicItemViews;
    
    @Nullable
    @Override
    protected ActivityRegisterBinding bindContentView(@Nullable Bundle bundle) {
        return ActivityRegisterBinding.inflate(getLayoutInflater());
    }

    @Override
    protected void subscribeViewModel(@Nullable Bundle bundle) {
        initCountryData();
        setupViews();
    }
    
    private void initCountryData() {
        countryList = new ArrayList<String>(Arrays.asList(
            "中国大陆", "中国香港", "中国澳门", "中国台湾",
            "新加坡", "美国", "日本", "越南", "巴西", 
            "法国", "英国", "德国", "加拿大", "澳大利亚",
            "韩国", "印度", "泰国", "马来西亚", "印度尼西亚"
        ));
        dynamicItemViews = new ArrayList<TPSingleLineItemView>();
    }
    
    private void setupViews() {
        viewBinding.btnNext.setEnabled(false);

        TextWatcher textWatcher = new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                // 文本改变前
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                // 文本改变中
            }

            @Override
            public void afterTextChanged(Editable s) {
                String email = viewBinding.account.getText().toString().trim();
                boolean isValidEmail = EmailValidator.isValidEmail(email);
                viewBinding.btnNext.setEnabled(!email.isEmpty() && isValidEmail);
            }
        };

        viewBinding.account.addTextChangedListener(textWatcher);

        // next 按钮跳转到 SetPasswordActivity
        viewBinding.btnNext.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String account = viewBinding.account.getText().toString();
                SharedPreferences sharedPreferences = getSharedPreferences("register_email", MODE_PRIVATE);
                SharedPreferences.Editor editor = sharedPreferences.edit();
                editor.putString("account", account);
                editor.apply();
                Intent intent = new Intent(RegisterActivity.this, SetPasswordActivity.class);
                startActivity(intent);
            }
        });

        viewBinding.btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(RegisterActivity.this, LoginActivity.class));
            }
        });

        // 地区选择按钮 - 动态版本
        viewBinding.tvRegion.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showCountrySelectionModal();
            }
        });
    }
    
    private void showCountrySelectionModal() {
        currentModal = new TPModalBottomSheet.Builder()
                .setScreenType(TPModalBottomSheet.ScreenType.FULL_SCREEN)
                .setDividerEnable(false)
                .setTitle("选择国家/地区")
                .setEndOptionText("完成")
                .setContentLayoutId(R.layout.fragment_region)
                .setContentViewListener(new TPModalBottomSheet.OnContentViewListener() {
                    @Override
                    public void onContentViewCreated(TPModalBottomSheet tpModalBottomSheet, View view) {
                        setupSearchView(view);
                        setupDynamicRadioGroup(view);
                    }
                })
                .show(getSupportFragmentManager(), "country_selection");
    }
    
    private void setupSearchView(View contentView) {
        // 根据你的XML布局,搜索框ID是tp_search
        TPSearchBar tpSearchBar = contentView.findViewById(R.id.tp_search);
        
        if (tpSearchBar == null) {
            Toast.makeText(this, "搜索框未找到,请检查布局文件", Toast.LENGTH_SHORT).show();
            return;
        }
        
        // 创建搜索适配器
        searchAdapter = new SearchViewBaseAdapter<String>(
            new ArrayList<String>(countryList),
            new SearchViewBaseAdapter.OnBindDataListener<String>() {
                @Override
                public void onBindViewHolder(String model, SearchViewBaseViewHolder viewHolder, int type, int position) {
                    viewHolder.setText(R.id.suggestion_text, model);
                }
                
                @Override
                public int getLayoutId(int type) {
                    return R.layout.layout_search_suggest_item;
                }
            }
        );
        
        // 设置搜索结果点击事件
        searchAdapter.setItemClickListener(new SearchViewBaseAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                String selectedCountry = searchAdapter.getData().get(position);
                selectCountry(selectedCountry);
            }
        });
        
        // 设置搜索框管理器和适配器
        tpSearchBar.setManager(getSupportFragmentManager());
        tpSearchBar.setSearchViewAdapter(searchAdapter);
        
        // 设置搜索字符实时监听 - 这是关键的搜索功能
        tpSearchBar.setTextChangeListener(new TPSearchBar.OnTextChangeListener() {
            @Override
            public void onTextChange(String searchText) {
                // 当用户输入时实时过滤国家列表
                filterCountries(contentView, searchText);
            }
        });
        
        // 设置搜索提交监听 - 当用户点击搜索按钮或回车时触发
        tpSearchBar.setOnQueryTextListener(new TPMaterialSearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                // 处理搜索提交,比如显示提示信息
                Toast.makeText(RegisterActivity.this, "搜索: " + query, Toast.LENGTH_SHORT).show();
                return false;
            }
            
            @Override
            public boolean onQueryTextChange(String newText) {
                // 这里也可以处理文本变化,但我们主要使用setTextChangeListener
                return false;
            }
        });
    }
    
    private void setupDynamicRadioGroup(View contentView) {
        TPListViewRadioGroup checkGroup = contentView.findViewById(R.id.check_group);
        
        // 清空现有的子视图
        checkGroup.removeAllViews();
        dynamicItemViews.clear();
        
        // 动态添加国家选项
        for (int i = 0; i < countryList.size(); i++) {
            final String country = countryList.get(i);
            TPSingleLineItemView itemView = new TPSingleLineItemView(this);
            itemView.setItemTitle(country);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    selectCountry(country);
                }
            });
            
            // 设置布局参数
            TPListViewRadioGroup.LayoutParams layoutParams = 
                new TPListViewRadioGroup.LayoutParams(
                    TPListViewRadioGroup.LayoutParams.MATCH_PARENT,
                    TPListViewRadioGroup.LayoutParams.WRAP_CONTENT
                );
            itemView.setLayoutParams(layoutParams);
            
            checkGroup.addView(itemView);
            dynamicItemViews.add(itemView);
        }
    }
    
    private void filterCountries(View contentView, String searchText) {
        if (searchText == null || searchText.trim().isEmpty()) {
            // 显示所有国家
            showAllCountries();
            searchAdapter.updateData(new ArrayList<String>(countryList));
        } else {
            // 过滤国家
            ArrayList<String> filteredList = getFilteredCountries(searchText);
            hideUnmatchedCountries(filteredList);
            searchAdapter.updateData(filteredList);
        }
    }
    
    private ArrayList<String> getFilteredCountries(String searchText) {
        ArrayList<String> filteredList = new ArrayList<String>();
        for (String country : countryList) {
            if (country.toLowerCase().contains(searchText.toLowerCase())) {
                filteredList.add(country);
            }
        }
        return filteredList;
    }
    
    private void showAllCountries() {
        for (TPSingleLineItemView itemView : dynamicItemViews) {
            itemView.setVisibility(View.VISIBLE);
        }
    }
    
    private void hideUnmatchedCountries(ArrayList<String> visibleCountries) {
        for (TPSingleLineItemView itemView : dynamicItemViews) {
            String itemTitle = itemView.getItemTitle();
            if (visibleCountries.contains(itemTitle)) {
                itemView.setVisibility(View.VISIBLE);
            } else {
                itemView.setVisibility(View.GONE);
            }
        }
    }
    
    private void selectCountry(String countryName) {
        // 更新显示的文本
        viewBinding.tvRegion.setText(countryName);
        
        // 保存选择的国家到SharedPreferences
        SharedPreferences sharedPreferences = getSharedPreferences("register_info", MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString("selected_country", countryName);
        editor.apply();
        
        // 显示选择提示
        Toast.makeText(this, "已选择: " + countryName, Toast.LENGTH_SHORT).show();
        
        // 关闭模态框
        dismissModal();
    }
    
    private void dismissModal() {
        if (currentModal != null) {
            Fragment fragment = getSupportFragmentManager().findFragmentByTag("country_selection");
            if (fragment != null) {
                getSupportFragmentManager().beginTransaction().remove(fragment).commit();
            }
        }
    }
}