ModalBottomSheet

244 阅读8分钟

package com.tplink.trainingapp.clients.viewmodel;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class ClientViewModel extends ViewModel {
    
    // 主网络在线设备数量
    private MutableLiveData<Integer> mainOnlineCount = new MutableLiveData<>(4);
    
    // 主网络离线设备数量  
    private MutableLiveData<Integer> mainOfflineCount = new MutableLiveData<>(3);
    
    // 访客网络设备数量
    private MutableLiveData<Integer> guestCount = new MutableLiveData<>(1);
    
    // 刷新状态
    private MutableLiveData<Boolean> isRefreshing = new MutableLiveData<>(false);
    
    // 获取主网络在线设备数量
    public LiveData<Integer> getMainOnlineCount() {
        return mainOnlineCount;
    }
    
    // 获取主网络离线设备数量
    public LiveData<Integer> getMainOfflineCount() {
        return mainOfflineCount;
    }
    
    // 获取访客网络设备数量
    public LiveData<Integer> getGuestCount() {
        return guestCount;
    }
    
    // 获取刷新状态
    public LiveData<Boolean> getRefreshingState() {
        return isRefreshing;
    }
    
    // 刷新数据
    public void refreshData() {
        isRefreshing.setValue(true);
        
        // 模拟网络请求延时
        // 在实际项目中,这里应该是网络请求或数据库查询
        new android.os.Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                // 模拟数据变化
                mainOnlineCount.setValue((int) (Math.random() * 5) + 1);
                mainOfflineCount.setValue((int) (Math.random() * 4) + 1);
                guestCount.setValue((int) (Math.random() * 3) + 1);
                
                isRefreshing.setValue(false);
            }
        }, 1500);
    }
    
    // 更新主网络在线设备数量
    public void updateMainOnlineCount(int count) {
        mainOnlineCount.setValue(count);
    }
    
    // 更新主网络离线设备数量
    public void updateMainOfflineCount(int count) {
        mainOfflineCount.setValue(count);
    }
    
    // 更新访客网络设备数量
    public void updateGuestCount(int count) {
        guestCount.setValue(count);
    }
    
    // 获取当前主网络在线设备数量的值
    public int getCurrentMainOnlineCount() {
        Integer count = mainOnlineCount.getValue();
        return count != null ? count : 0;
    }
    
    // 获取当前主网络离线设备数量的值
    public int getCurrentMainOfflineCount() {
        Integer count = mainOfflineCount.getValue();
        return count != null ? count : 0;
    }
    
    // 获取当前访客网络设备数量的值
    public int getCurrentGuestCount() {
        Integer count = guestCount.getValue();
        return count != null ? count : 0;
    }
}



package com.tplink.trainingapp.clients.adapter;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ImageSpan;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;

import com.tplink.design.list.TPTwoLineItemView;
import com.tplink.design.list.TPTwoLineItemViewHolder;
import com.tplink.design.menu.TPPopupMenu;
import com.tplink.trainingapp.R;

import java.text.SimpleDateFormat;
import java.util.Date;

public class ClientListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
    private int itemCount;
    private Context context;
    private boolean isOnlineAdapter; // 区分在线和离线adapter
    
    // 模拟的设备数据
    private String[] deviceNames = {"Melanie's iPhone", "Melanie's iMac", "Melanie's iPhone"};
    private int[] deviceTypes = {0, 1, 0}; // 0-手机, 1-电脑
    private int[] signalStrength = {3, 2, 1};
    private int[] downloadRates = {50, 50, 0};
    private int[] uploadRates = {25, 25, 0};
    
    public ClientListAdapter(int itemCount, Context context, boolean isOnlineAdapter) {
        this.itemCount = itemCount;
        this.context = context;
        this.isOnlineAdapter = isOnlineAdapter;
        this.context = context;
    }
    
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return TPTwoLineItemViewHolder.create(parent);
    }
    
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        TPTwoLineItemView lineItem = (TPTwoLineItemView) holder.itemView;
        
        // 直接使用数组中的模拟数据
        String deviceName = deviceNames[position % deviceNames.length];
        int deviceType = deviceTypes[position % deviceTypes.length];
        int signal = signalStrength[position % signalStrength.length];
        int downloadRate = downloadRates[position % downloadRates.length];
        int uploadRate = uploadRates[position % uploadRates.length];
        
        // 根据adapter类型决定status
        int status = isOnlineAdapter ? 0 : 1; // 0-在线, 1-离线
        
        // 设置设备类型图标
        // DEVICE_TYPE_MOBILE_PHONE = 0, DEVICE_TYPE_DESKTOP = 1
        if (deviceType == 1) { // DEVICE_TYPE_DESKTOP
            lineItem.setStartIcon(R.mipmap.icon_laptop);
        } else { // DEVICE_TYPE_MOBILE_PHONE
            lineItem.setStartIcon(R.mipmap.icon_games);
        }
        
        // 设置图标尺寸
        ViewGroup.LayoutParams layoutParams = lineItem.getStartIcon().getLayoutParams();
        int iconSize = (int) context.getResources().getDimension(com.tplink.design.R.dimen.tpds_all_dp_40);
        layoutParams.width = iconSize;
        layoutParams.height = iconSize;
        lineItem.getStartIcon().setLayoutParams(layoutParams);
        
        lineItem.setTitleText(deviceName);
        lineItem.showDivider(true);
        
        if (status == 0) { // 在线设备
            lineItem.setEndIcon(getMyEndIcon(signal));
            setItemContentOnline(uploadRate, downloadRate, lineItem);
        } else { // 离线设备
            lineItem.setEndIcon(R.drawable.toolbar_icon_next);
            setItemContentOffline(System.currentTimeMillis() - 24 * 60 * 60 * 1000, lineItem); // 一天前
        }
        
        // 设置长按事件
        switch (status) {
            case 0:
                lineItem.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        new TPPopupMenu(lineItem.getContext(), v, R.menu.clients_online_menu)
                            .setIconEnable(true)
                            .show();
                        return true;
                    }
                });
                break;
            case 1:
                lineItem.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        new TPPopupMenu(lineItem.getContext(), v, R.menu.clients_offline_menu)
                            .setIconEnable(true)
                            .show();
                        return true;
                    }
                });
                break;
        }
    }
    
    @Override
    public int getItemCount() {
        return itemCount;
    }
    
    // 设置在线设备content部分的TextView为 icon+text+icon+text的形式
    private void setItemContentOnline(int uploadRate, int downloadRate, TPTwoLineItemView lineItem) {
        TextView textView = lineItem.getContent();
        android.graphics.Paint.FontMetrics fm = textView.getPaint().getFontMetrics();
        float textHeight = fm.descent - fm.ascent;
        
        String downloadText = " " + downloadRate + "KB/s";
        String spaceText = "  ";
        String uploadText = " " + uploadRate + "KB/s";
        
        SpannableString spannable = new SpannableString(downloadText + spaceText + uploadText);
        
        Drawable uploadIconDrawable = ContextCompat.getDrawable(context, R.drawable.icon_upload_no_background);
        Drawable downloadIconDrawable = ContextCompat.getDrawable(context, R.drawable.icon_download);
        
        if (uploadIconDrawable != null && downloadIconDrawable != null) {
            float iconAspectRatio = (float) uploadIconDrawable.getIntrinsicWidth() / uploadIconDrawable.getIntrinsicHeight();
            int scaleWidth = (int) (textHeight * iconAspectRatio);
            
            downloadIconDrawable.setBounds(0, 0, scaleWidth, (int) textHeight);
            uploadIconDrawable.setBounds(0, 0, scaleWidth, (int) textHeight);
            
            ImageSpan uploadIconSpan = new ImageSpan(uploadIconDrawable, ImageSpan.ALIGN_BOTTOM);
            ImageSpan downloadIconSpan = new ImageSpan(downloadIconDrawable, ImageSpan.ALIGN_BOTTOM);
            
            spannable.setSpan(downloadIconSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            spannable.setSpan(
                uploadIconSpan,
                downloadText.length() + spaceText.length(),
                downloadText.length() + spaceText.length() + 1,
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
            );
        }
        
        textView.setText(spannable);
        lineItem.getContent().setVisibility(View.VISIBLE);
    }
    
    // 设置离线设备content部分的TextView为上次登录时间
    @SuppressLint("SimpleDateFormat")
    private void setItemContentOffline(long lastOnlineTime, TPTwoLineItemView lineItem) {
        SimpleDateFormat timeFormat = new SimpleDateFormat(context.getString(R.string.clients_time_format_pattern));
        String timeString = timeFormat.format(new Date(lastOnlineTime));
        String contentText = context.getString(R.string.clients_hint_current_online_time, timeString);
        lineItem.setContentText(contentText);
    }
    
    // 设置endIcon为 signal icon + next icon的形式
    private LayerDrawable getMyEndIcon(int strength) {
        Drawable leftIcon;
        switch (strength) {
            case 1:
                leftIcon = ContextCompat.getDrawable(context, R.drawable.icon_wifi_strength_1);
                break;
            case 2:
                leftIcon = ContextCompat.getDrawable(context, R.drawable.icon_wifi_strength_2);
                break;
            case 3:
                leftIcon = ContextCompat.getDrawable(context, R.drawable.icon_wifi_strength_3);
                break;
            default:
                leftIcon = ContextCompat.getDrawable(context, R.drawable.icon_wifi_strength_3);
                break;
        }
        
        Drawable rightIcon = ContextCompat.getDrawable(context, R.drawable.toolbar_icon_next);
        Drawable[] layers = {leftIcon, rightIcon};
        LayerDrawable layerDrawable = new LayerDrawable(layers);
        
        int iconSize = (int) context.getResources().getDimension(com.tplink.design.R.dimen.tpds_all_dp_20);
        int spacing = (int) context.getResources().getDimension(com.tplink.design.R.dimen.tpds_all_dp_20);
        
        layerDrawable.setLayerSize(0, iconSize, iconSize);
        layerDrawable.setLayerGravity(0, Gravity.CENTER_HORIZONTAL);
        layerDrawable.setLayerGravity(1, Gravity.CENTER_HORIZONTAL);
        layerDrawable.setLayerInset(0, -spacing, 0, 0, 0);
        layerDrawable.setLayerInset(1, 2 * spacing, 0, 0, 0);
        
        return layerDrawable;
    }
}



package com.tplink.trainingapp.clients.adapter;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ImageSpan;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;

import com.tplink.design.list.TPTwoLineItemView;
import com.tplink.design.list.TPTwoLineItemViewHolder;
import com.tplink.design.menu.TPPopupMenu;
import com.tplink.trainingapp.R;
import com.tplink.trainingapp.clients.bean.ClientInfo;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class ClientListAdapterNormal extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
    private List<ClientInfo> clientList;
    private Context context;
    
    public ClientListAdapterNormal(List<ClientInfo> list, Context context) {
        this.clientList = new ArrayList<>();
        this.clientList.addAll(list);
        this.context = context;
    }
    
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return TPTwoLineItemViewHolder.create(parent);
    }
    
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        TPTwoLineItemView lineItem = (TPTwoLineItemView) holder.itemView;
        ClientInfo clientInfo = clientList.get(position);
        
        // 设置设备类型图标
        if (clientInfo.getDeviceType() == ClientInfo.DEVICE_TYPE_DESKTOP) {
            lineItem.setStartIcon(R.mipmap.icon_laptop);
        } else {
            lineItem.setStartIcon(R.mipmap.icon_games);
        }
        
        // 设置图标尺寸
        ViewGroup.LayoutParams layoutParams = lineItem.getStartIcon().getLayoutParams();
        int iconSize = (int) context.getResources().getDimension(com.tplink.design.R.dimen.tpds_all_dp_40);
        layoutParams.width = iconSize;
        layoutParams.height = iconSize;
        lineItem.getStartIcon().setLayoutParams(layoutParams);
        
        lineItem.setTitleText(clientInfo.getDeviceName());
        lineItem.showDivider(true);
        
        if (clientInfo.getStatus() == 0) {
            // 在线设备
            lineItem.setEndIcon(getMyEndIcon(clientInfo.getSignalStrength()));
            setItemContentOnline(
                clientInfo.getUploadRate(),
                clientInfo.getDownloadRate(),
                lineItem
            );
        } else {
            // 离线设备
            lineItem.setEndIcon(R.drawable.toolbar_icon_next);
            setItemContentOffline(clientInfo.getLastOnlineTime(), lineItem);
        }
        
        // 设置长按事件
        switch (clientInfo.getStatus()) {
            case 0:
                lineItem.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        new TPPopupMenu(lineItem.getContext(), v, R.menu.clients_online_menu)
                            .setIconEnable(true)
                            .show();
                        return true;
                    }
                });
                break;
            case 1:
                lineItem.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        new TPPopupMenu(lineItem.getContext(), v, R.menu.clients_offline_menu)
                            .setIconEnable(true)
                            .show();
                        return true;
                    }
                });
                break;
        }
    }
    
    @Override
    public int getItemCount() {
        return clientList.size();
    }
    
    // 设置在线设备content部分的TextView为 icon+text+icon+text的形式
    private void setItemContentOnline(int uploadRate, int downloadRate, TPTwoLineItemView lineItem) {
        TextView textView = lineItem.getContent();
        android.graphics.Paint.FontMetrics fm = textView.getPaint().getFontMetrics();
        float textHeight = fm.descent - fm.ascent;
        
        String downloadText = " " + downloadRate + "KB/s";
        String spaceText = "  ";
        String uploadText = " " + uploadRate + "KB/s";
        
        SpannableString spannable = new SpannableString(downloadText + spaceText + uploadText);
        
        Drawable uploadIconDrawable = ContextCompat.getDrawable(context, R.drawable.icon_upload_no_background);
        Drawable downloadIconDrawable = ContextCompat.getDrawable(context, R.drawable.icon_download);
        
        if (uploadIconDrawable != null && downloadIconDrawable != null) {
            float iconAspectRatio = (float) uploadIconDrawable.getIntrinsicWidth() / uploadIconDrawable.getIntrinsicHeight();
            int scaleWidth = (int) (textHeight * iconAspectRatio);
            
            downloadIconDrawable.setBounds(0, 0, scaleWidth, (int) textHeight);
            uploadIconDrawable.setBounds(0, 0, scaleWidth, (int) textHeight);
            
            ImageSpan uploadIconSpan = new ImageSpan(uploadIconDrawable, ImageSpan.ALIGN_BOTTOM);
            ImageSpan downloadIconSpan = new ImageSpan(downloadIconDrawable, ImageSpan.ALIGN_BOTTOM);
            
            spannable.setSpan(downloadIconSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            spannable.setSpan(
                uploadIconSpan,
                downloadText.length() + spaceText.length(),
                downloadText.length() + spaceText.length() + 1,
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
            );
        }
        
        textView.setText(spannable);
        lineItem.getContent().setVisibility(View.VISIBLE);
    }
    
    // 设置离线设备content部分的TextView为上次登录时间
    @SuppressLint("SimpleDateFormat")
    private void setItemContentOffline(long lastOnlineTime, TPTwoLineItemView lineItem) {
        SimpleDateFormat timeFormat = new SimpleDateFormat(context.getString(R.string.clients_time_format_pattern));
        String timeString = timeFormat.format(new Date(lastOnlineTime));
        String contentText = context.getString(R.string.clients_hint_current_online_time, timeString);
        lineItem.setContentText(contentText);
    }
    
    // 设置endIcon为 signal icon + next icon的形式
    private LayerDrawable getMyEndIcon(int strength) {
        Drawable leftIcon;
        switch (strength) {
            case 1:
                leftIcon = ContextCompat.getDrawable(context, R.drawable.icon_wifi_strength_1);
                break;
            case 2:
                leftIcon = ContextCompat.getDrawable(context, R.drawable.icon_wifi_strength_2);
                break;
            case 3:
                leftIcon = ContextCompat.getDrawable(context, R.drawable.icon_wifi_strength_3);
                break;
            default:
                leftIcon = ContextCompat.getDrawable(context, R.drawable.icon_wifi_strength_3);
                break;
        }
        
        Drawable rightIcon = ContextCompat.getDrawable(context, R.drawable.toolbar_icon_next);
        Drawable[] layers = {leftIcon, rightIcon};
        LayerDrawable layerDrawable = new LayerDrawable(layers);
        
        int iconSize = (int) context.getResources().getDimension(com.tplink.design.R.dimen.tpds_all_dp_20);
        int spacing = (int) context.getResources().getDimension(com.tplink.design.R.dimen.tpds_all_dp_20);
        
        layerDrawable.setLayerSize(0, iconSize, iconSize);
        layerDrawable.setLayerGravity(0, Gravity.CENTER_HORIZONTAL);
        layerDrawable.setLayerGravity(1, Gravity.CENTER_HORIZONTAL);
        layerDrawable.setLayerInset(0, -spacing, 0, 0, 0);
        layerDrawable.setLayerInset(1, 2 * spacing, 0, 0, 0);
        
        return layerDrawable;
    }
}

package com.tplink.trainingapp.clients.view;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;

import com.tplink.apps.architecture.BaseMvvmFragment;
import com.tplink.trainingapp.clients.adapter.ClientListAdapterNormal;
import com.tplink.trainingapp.clients.viewmodel.ClientViewModel;
import com.tplink.trainingapp.databinding.FragmentClientsGuestBinding;

public class ClientGuestFragment extends BaseMvvmFragment<FragmentClientsGuestBinding> {
    
    private ClientViewModel viewModel;
    private ClientListAdapterNormal adapter;
    
    @NonNull
    @Override
    protected FragmentClientsGuestBinding bindView(@NonNull LayoutInflater inflater, 
                                                   @Nullable ViewGroup container, 
                                                   @Nullable Bundle savedInstanceState) {
        return FragmentClientsGuestBinding.inflate(inflater, container, false);
    }
    
    @Override
    protected void subscribeViewModel(@Nullable Bundle savedInstanceState) {
        viewModel = new ViewModelProvider(requireActivity()).get(ClientViewModel.class);
        initView();
    }
    
    private void initView() {
        adapter = new ClientListAdapterNormal(
            viewModel.getGuestClients(), 
            requireActivity().getBaseContext()
        );
        viewBinding.rvOnline.setAdapter(adapter);
    }
}

package com.tplink.trainingapp.clients.view;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;

import com.tplink.apps.architecture.BaseMvvmFragment;
import com.tplink.design.menu.TPPopupMenu;
import com.tplink.trainingapp.R;
import com.tplink.trainingapp.clients.adapter.ClientListAdapterNormal;
import com.tplink.trainingapp.clients.viewmodel.ClientViewModel;
import com.tplink.trainingapp.databinding.FragmentClientsMainBinding;

public class ClientMainFragment extends BaseMvvmFragment<FragmentClientsMainBinding> {
    
    private ClientViewModel viewModel;
    private ClientListAdapterNormal onlineAdapter;
    private ClientListAdapterNormal offlineAdapter;
    
    @Override
    protected FragmentClientsMainBinding bindView(LayoutInflater inflater, 
                                                  @Nullable ViewGroup container, 
                                                  @Nullable Bundle savedInstanceState) {
        return FragmentClientsMainBinding.inflate(inflater, container, false);
    }
    
    @Override
    protected void subscribeViewModel(@Nullable Bundle savedInstanceState) {
        viewModel = new ViewModelProvider(requireActivity()).get(ClientViewModel.class);
        initView();
    }
    
    private void initView() {
        onlineAdapter = new ClientListAdapterNormal(
            viewModel.getMainClientsOnline(), 
            requireActivity().getBaseContext()
        );
        viewBinding.rvOnline.setAdapter(onlineAdapter);
        
        offlineAdapter = new ClientListAdapterNormal(
            viewModel.getMainClientsOffline(), 
            requireActivity().getBaseContext()
        );
        viewBinding.rvOffline.setAdapter(offlineAdapter);
        
        viewBinding.btnOffline.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new TPPopupMenu(requireContext(), v, R.menu.clients_online_menu)
                    .setIconEnable(true)
                    .show();
            }
        });
    }
}

// 原来的按钮点击事件现在变得非常简洁
viewBinding.tvRegion.setOnClickListener(v -> showCountrySelectionModal());

private void showCountrySelectionModal() {
    CountrySelectionFragment fragment = CountrySelectionFragment.newInstance();
    fragment.setCountryViewModel(countryViewModel);
    fragment.setOnCountrySelectedListener(countryName -> {
        // 处理国家选择结果
        // countryViewModel.selectCountry(countryName); // 这个在Fragment内部已经处理了
        // 可以在这里添加其他需要的逻辑,比如更新UI
        updateRegionDisplay();
    });
    fragment.show(getSupportFragmentManager(), "country_selection");
}

private void updateRegionDisplay() {
    // 更新显示选中的地区
    String selectedCountry = countryViewModel.getCurrentSelectedCountry();
    if (selectedCountry != null) {
        viewBinding.tvRegion.setText(selectedCountry);
    }
}
public class CountrySelectionFragment extends TPModalBottomSheet {
    
    private CountryViewModel countryViewModel;
    private OnCountrySelectedListener listener;
    
    // 回调接口
    public interface OnCountrySelectedListener {
        void onCountrySelected(String countryName);
    }
    
    public static CountrySelectionFragment newInstance() {
        CountrySelectionFragment fragment = new CountrySelectionFragment();
        return fragment;
    }
    
    public void setCountryViewModel(CountryViewModel viewModel) {
        this.countryViewModel = viewModel;
    }
    
    public void setOnCountrySelectedListener(OnCountrySelectedListener listener) {
        this.listener = listener;
    }
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 不直接设置属性,通过重写方法来实现
    }
    
    // 重写初始化TopBar的方法
    @Override
    public void initTopBar() {
        super.initTopBar();
        // 在这里设置TopBar相关属性
        if (topBar != null) {
            topBar.setTitle("Region");
            topBar.setEndOptionText("Done");
        }
        // 设置分隔线不显示
        if (topBarDivider != null) {
            topBarDivider.setVisibility(View.GONE);
        }
    }
    
    @Override
    public void onContentViewCreated(View view, Bundle savedInstanceState) {
        super.onContentViewCreated(view, savedInstanceState);
        
        if (countryViewModel != null) {
            setupCountrySelectionUI(view);
        }
    }
    
    // 重写这个方法来指定布局ID
    @Override
    protected int getLayoutResId() {
        return R.layout.fragment_region;
    }
    
    // 重写这个方法来指定屏幕类型
    protected ScreenType getScreenType() {
        return ScreenType.FULL_SCREEN;
    }
    
    private void setupCountrySelectionUI(View contentView) {
        RecyclerView recyclerView = contentView.findViewById(R.id.recyclerView_countries);
        CountryAdapter countryAdapter = new CountryAdapter(countryViewModel.getAllCountries());
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        recyclerView.setAdapter(countryAdapter);
        
        // 设置当前选中的国家
        String currentCountry = countryViewModel.getCurrentSelectedCountry();
        if (currentCountry != null && !currentCountry.trim().isEmpty()) {
            countryAdapter.setSelectedCountry(currentCountry);
        }
        
        countryAdapter.setOnCountryClickListener(new CountryAdapter.OnCountryClickListener() {
            @Override
            public void onCountryClick(String countryName, int position) {
                countryViewModel.selectCountry(countryName);
                // 通知外部监听器
                if (listener != null) {
                    listener.onCountrySelected(countryName);
                }
                // 关闭对话框
                dismiss();
            }
        });
        
        // 观察过滤后的国家列表变化,实时更新RecyclerView
        countryViewModel.filteredCountries.observe(this, countries -> {
            countryAdapter.updateData(countries);
            if(countries.size() == countryViewModel.getAllCountries().size()){
                String selectedCountry = countryViewModel.getCurrentSelectedCountry();
                if(selectedCountry != null && !selectedCountry.isEmpty()){
                    int selectedPosition = countryAdapter.getSelectedPosition();
                    if(selectedPosition >= 0){
                        recyclerView.post(() -> {
                            recyclerView.smoothScrollToPosition(selectedPosition);
                        });
                    }
                }
            }
        });
        
        countryViewModel.selectedCountry.observe(this, selectedCountry -> {
            countryAdapter.setSelectedCountry(selectedCountry);
        });
        
        setupSearchView(contentView, countryAdapter);
    }
    
    /**
     * 设置搜索功能
     */
    private void setupSearchView(View contentView, CountryAdapter countryAdapter) {
        TPSearchBar tpSearchBar = contentView.findViewById(R.id.tp_search);
        
        // 创建搜索建议数据
        ArrayList<String> searchData = new ArrayList<>(countryViewModel.getAllCountries());
        
        // 创建搜索适配器
        SearchViewBaseAdapter<String> searchAdapter = new SearchViewBaseAdapter<String>(
                searchData,
                new SearchViewBaseAdapter.OnBindDataListener<String>() {
                    @Override
                    public void onBindViewHolder(String model, SearchViewBaseViewHolder viewHolder, int type, int position) {
                        viewHolder.setText(R.id.tv_country_name, model);
                    }
                    
                    @Override
                    public int getLayoutId(int type) {
                        return R.layout.item_country;
                    }
                }
        );
        
        // 设置搜索结果点击事件
        searchAdapter.setItemClickListener(new SearchViewBaseAdapter.OnItemClickListener<String>() {
            @Override
            public void onClick(View view, String data) {
                // 通过ViewModel选择国家
                countryViewModel.selectCountry(data);
                countryAdapter.setSelectedCountry(data);
                tpSearchBar.dismissSearchView();
                countryViewModel.clearSearch();
                
                // 通知外部监听器
                if (listener != null) {
                    listener.onCountrySelected(data);
                }
                // 关闭对话框
                dismiss();
            }
        });
        
        // 设置搜索字符实时监听
        tpSearchBar.setTextChangeListener(new TPMaterialSearchView.TextChangeListener() {
            @Override
            public void onTextChange(String searchText) {
                countryViewModel.filterCountries(searchText);
                // 过滤搜索建议
                if (searchText == null || searchText.trim().isEmpty()) {
                    searchAdapter.updateData(new ArrayList<>(countryViewModel.getAllCountries()));
                } else {
                    ArrayList<String> filteredList = getFilteredCountries(searchText);
                    searchAdapter.updateData(filteredList);
                }
            }
        });
        
        tpSearchBar.setManager(getChildFragmentManager());
        tpSearchBar.setSearchViewAdapter(searchAdapter);
        tpSearchBar.setOnQueryTextListener(new TPMaterialSearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }
            
            @Override
            public boolean onQueryTextChange(String newText) {
                return false;
            }
        });
    }
    
    private ArrayList<String> getFilteredCountries(String searchText) {
        ArrayList<String> filteredList = new ArrayList<>();
        if (searchText != null && !searchText.trim().isEmpty()) {
            String searchLower = searchText.toLowerCase().trim();
            for (String country : countryViewModel.getAllCountries()) {
                if (country != null && country.toLowerCase().contains(searchLower)) {
                    filteredList.add(country);
                }
            }
        } else {
            filteredList.addAll(countryViewModel.getAllCountries());
        }
        return filteredList;
    }
}

public class CountrySelectionFragment extends TPModalBottomSheet {
    
    private CountryViewModel countryViewModel;
    private OnCountrySelectedListener listener;
    
    // 回调接口
    public interface OnCountrySelectedListener {
        void onCountrySelected(String countryName);
    }
    
    public static CountrySelectionFragment newInstance() {
        CountrySelectionFragment fragment = new CountrySelectionFragment();
        return fragment;
    }
    
    public void setCountryViewModel(CountryViewModel viewModel) {
        this.countryViewModel = viewModel;
    }
    
    public void setOnCountrySelectedListener(OnCountrySelectedListener listener) {
        this.listener = listener;
    }
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 设置模态框属性
        screenType = ScreenType.FULL_SCREEN;
        dividerEnable = false;
        topBarTitleText = "Region";
        topBarEndText = "Done";
        contentLayoutId = R.layout.fragment_region;
    }
    
    @Override
    public void onContentViewCreated(View view, Bundle savedInstanceState) {
        super.onContentViewCreated(view, savedInstanceState);
        if (countryViewModel != null) {
            setupCountrySelectionUI(view);
        }
    }
    
    private void setupCountrySelectionUI(View contentView) {
        RecyclerView recyclerView = contentView.findViewById(R.id.recyclerView_countries);
        CountryAdapter countryAdapter = new CountryAdapter(countryViewModel.getAllCountries());
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        recyclerView.setAdapter(countryAdapter);
        
        // 设置当前选中的国家
        String currentCountry = countryViewModel.getCurrentSelectedCountry();
        if (currentCountry != null && !currentCountry.trim().isEmpty()) {
            countryAdapter.setSelectedCountry(currentCountry);
        }
        
        countryAdapter.setOnCountryClickListener(new CountryAdapter.OnCountryClickListener() {
            @Override
            public void onCountryClick(String countryName, int position) {
                countryViewModel.selectCountry(countryName);
                // 通知外部监听器
                if (listener != null) {
                    listener.onCountrySelected(countryName);
                }
                // 关闭对话框
                dismiss();
            }
        });
        
        // 观察过滤后的国家列表变化,实时更新RecyclerView
        countryViewModel.filteredCountries.observe(this, countries -> {
            countryAdapter.updateData(countries);
            if(countries.size() == countryViewModel.getAllCountries().size()){
                String selectedCountry = countryViewModel.getCurrentSelectedCountry();
                if(selectedCountry != null && !selectedCountry.isEmpty()){
                    int selectedPosition = countryAdapter.getSelectedPosition();
                    if(selectedPosition >= 0){
                        recyclerView.post(() -> {
                            recyclerView.smoothScrollToPosition(selectedPosition);
                        });
                    }
                }
            }
        });
        
        countryViewModel.selectedCountry.observe(this, selectedCountry -> {
            countryAdapter.setSelectedCountry(selectedCountry);
        });
        
        setupSearchView(contentView, countryAdapter);
    }
    
    /**
     * 设置搜索功能
     */
    private void setupSearchView(View contentView, CountryAdapter countryAdapter) {
        TPSearchBar tpSearchBar = contentView.findViewById(R.id.tp_search);
        
        // 创建搜索建议数据
        ArrayList<String> searchData = new ArrayList<>(countryViewModel.getAllCountries());
        
        // 创建搜索适配器
        SearchViewBaseAdapter<String> searchAdapter = new SearchViewBaseAdapter<String>(
                searchData,
                new SearchViewBaseAdapter.OnBindDataListener<String>() {
                    @Override
                    public void onBindViewHolder(String model, SearchViewBaseViewHolder viewHolder, int type, int position) {
                        viewHolder.setText(R.id.tv_country_name, model);
                    }
                    
                    @Override
                    public int getLayoutId(int type) {
                        return R.layout.item_country;
                    }
                }
        );
        
        // 设置搜索结果点击事件
        searchAdapter.setItemClickListener(new SearchViewBaseAdapter.OnItemClickListener<String>() {
            @Override
            public void onClick(View view, String data) {
                // 通过ViewModel选择国家
                countryViewModel.selectCountry(data);
                countryAdapter.setSelectedCountry(data);
                tpSearchBar.dismissSearchView();
                countryViewModel.clearSearch();
                
                // 通知外部监听器
                if (listener != null) {
                    listener.onCountrySelected(data);
                }
                // 关闭对话框
                dismiss();
            }
        });
        
        // 设置搜索字符实时监听
        tpSearchBar.setTextChangeListener(new TPMaterialSearchView.TextChangeListener() {
            @Override
            public void onTextChange(String searchText) {
                countryViewModel.filterCountries(searchText);
                // 过滤搜索建议
                if (searchText == null || searchText.trim().isEmpty()) {
                    searchAdapter.updateData(new ArrayList<>(countryViewModel.getAllCountries()));
                } else {
                    ArrayList<String> filteredList = getFilteredCountries(searchText);
                    searchAdapter.updateData(filteredList);
                }
            }
        });
        
        tpSearchBar.setManager(getChildFragmentManager());
        tpSearchBar.setSearchViewAdapter(searchAdapter);
        tpSearchBar.setOnQueryTextListener(new TPMaterialSearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }
            
            @Override
            public boolean onQueryTextChange(String newText) {
                return false;
            }
        });
    }
    
    private ArrayList<String> getFilteredCountries(String searchText) {
        ArrayList<String> filteredList = new ArrayList<>();
        if (searchText != null && !searchText.trim().isEmpty()) {
            String searchLower = searchText.toLowerCase().trim();
            for (String country : countryViewModel.getAllCountries()) {
                if (country != null && country.toLowerCase().contains(searchLower)) {
                    filteredList.add(country);
                }
            }
        } else {
            filteredList.addAll(countryViewModel.getAllCountries());
        }
        return filteredList;
    }
}

主要变化:

  1. 属性设置方式 - 直接给protected字段赋值,而不是调用方法:

    screenType = ScreenType.FULL_SCREEN;  // 而不是setScreenType()
    dividerEnable = false;                // 而不是setDividerEnable()
    topBarTitleText = "Region";          // 而不是setTitle()
    
  2. 布局设置 - 使用contentLayoutId字段:

    contentLayoutId = R.layout.fragment_region;
    
  3. UI初始化 - 重写onContentViewCreated方法,这是TPModalBottomSheet的标准生命周期方法