You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义对象ArrayList的code属性判断引发布局与题号显示异常

问题分析与解决方案

我帮你梳理下遇到的两个核心问题的根源,然后给出针对性的修复方案:

问题1:第二题及后续布局错乱、题号显示异常

原因:

  1. 布局参数复用导致规则残留:你全局定义了submit_params和main_params,每次加载题目时只是在原有对象上修改规则,之前的布局规则没有被完全清除,导致后续布局叠加错乱。
  2. 题号计数逻辑错误:curr_quesNo在每次loadQues时都自增,而count是提交后才递增,当题目加载出现异常或者重复调用loadQues时,两者会不同步,导致题号显示错误(比如显示成2但实际还是第一题)。

问题2:仅code列有内容的题目能加载

原因:

当数据库中code字段为null时,调用arrQues.get(count).code.isEmpty()会抛出NullPointerException,触发try-catch块里的代码重新创建QuestionsFragment,导致加载流程中断,只有code不为null的题目能正常加载完成。


修复后的完整代码及关键修改点

关键修改说明:

  1. 每次加载题目时重新创建LayoutParams:不再复用全局的布局参数对象,彻底避免规则残留。
  2. 修正题号计数逻辑:让curr_quesNo直接等于count + 1,保证和当前题目索引完全同步。
  3. 安全判断code字段:先判断code是否为null,再判断是否为空字符串,避免空指针异常。
  4. 移除异常时重新创建Fragment的逻辑:改为打印异常信息并提示用户,避免加载流程被中断。
public class QuestionsFragment extends Fragment implements View.OnClickListener {
    TextView txtTimer, txtStatus;
    LinearLayout boxA, boxB, boxC, boxD, mainContainer;
    RelativeLayout submitContainer;
    ScrollView scrollView;
    Button btnSubmit;
    DBHelper dbHelper;
    SharedPreferences sharedPreferences;
    TextView txtQues;
    WebView txtCode;
    TextView txtOptA, txtOptB, txtOptC, txtOptD;
    String ans;
    ArrayList<QuestionModal> arrQues = new ArrayList<>();
    ArrayList<String> arrAnswers = new ArrayList<>();
    CountDownTimer countDownTimer;
    boolean timerSwitch;
    int selectedVal, id;
    int curr_quesNo = 0;
    int count = 0;
    int right = 0;
    int non_attempted = 0;

    public QuestionsFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_questions, container, false);
        txtQues = view.findViewById(R.id.txtQues);
        txtOptA = view.findViewById(R.id.txtOptionA);
        txtOptB = view.findViewById(R.id.txtOptionB);
        txtOptC = view.findViewById(R.id.txtOptionC);
        txtOptD = view.findViewById(R.id.txtOptionD);
        txtCode = view.findViewById(R.id.txtCode);
        txtStatus = view.findViewById(R.id.txtStatus);
        boxA = view.findViewById(R.id.boxA);
        boxB = view.findViewById(R.id.boxB);
        boxC = view.findViewById(R.id.boxC);
        boxD = view.findViewById(R.id.boxD);
        scrollView = view.findViewById(R.id.scrollView);
        btnSubmit = view.findViewById(R.id.btnSubmit);
        submitContainer = view.findViewById(R.id.submitContainer);
        mainContainer = view.findViewById(R.id.mainContainer);

        sharedPreferences = getActivity().getSharedPreferences("PrefFile", MODE_PRIVATE);
        timerSwitch = sharedPreferences.getBoolean("timer_switch", true);
        selectedVal = sharedPreferences.getInt("selectedVal", 10);

        dbHelper = DBHelper.getDB(getActivity(), sharedPreferences.getString("db_name", null));
        if (!dbHelper.checkDB()) {
            dbHelper.createDB(getActivity());
        }
        dbHelper.openDB();

        String levelKey = sharedPreferences.getString("level_key", null);
        arrQues = dbHelper.getQues(levelKey, selectedVal);

        loadQues(timerSwitch);

        txtTimer = view.findViewById(R.id.txtTimer);

        switch (sharedPreferences.getString("db_name", null)) {
            case "Android":
                ((MainActivity) getActivity()).setFragTitle("Android Quiz");
                break;
            case "Java":
                ((MainActivity) getActivity()).setFragTitle("Java Quiz");
                break;
            case "C":
                ((MainActivity) getActivity()).setFragTitle("C Quiz");
                ((MainActivity) getActivity()).setFragLogo(R.drawable.ic_home_black_24dp);
                break;
            case "C++":
                ((MainActivity) getActivity()).setFragTitle("C++ Quiz");
                break;
            case "Python":
                ((MainActivity) getActivity()).setFragTitle("Python Quiz");
                break;
            case "Kotlin":
                ((MainActivity) getActivity()).setFragTitle("Kotlin Quiz");
                break;
        }

        btnSubmit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (timerSwitch) countDownTimer.cancel();

                if (id == 0) {
                    non_attempted++;
                    arrAnswers.add("NotAttempted");
                    Toast.makeText(getActivity(), "Not Attempted!", Toast.LENGTH_SHORT).show();
                }

                switch (id) {
                    case R.id.boxA:
                        arrAnswers.add("A");
                        break;
                    case R.id.boxB:
                        arrAnswers.add("B");
                        break;
                    case R.id.boxC:
                        arrAnswers.add("C");
                        break;
                    case R.id.boxD:
                        arrAnswers.add("D");
                        break;
                }

                if ((id == R.id.boxA && ans.equals("A")) || (id == R.id.boxB && ans.equals("B")) || (id == R.id.boxC && ans.equals("C")) || (id == R.id.boxD && ans.equals("D"))) {
                    right++;
                }

                count++;
                if (count < arrQues.size()) {
                    loadQues(timerSwitch);
                } else {
                    sendResult();
                }
            }
        });

        return view;
    }

    public void setBtnDefault() {
        boxA.setBackgroundColor(getResources().getColor(android.R.color.transparent));
        boxB.setBackgroundColor(getResources().getColor(android.R.color.transparent));
        boxC.setBackgroundColor(getResources().getColor(android.R.color.transparent));
        boxD.setBackgroundColor(getResources().getColor(android.R.color.transparent));
    }

    public void sendResult() {
        int attempted = selectedVal - non_attempted;
        Gson gson = new Gson();
        String jsonAnswers = gson.toJson(arrAnswers);
        String jsonQues = gson.toJson(arrQues);

        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putInt("right_key", right);
        editor.putInt("wrong_key", attempted - right);
        editor.putInt("total_key", selectedVal);
        editor.putInt("attempted_key", attempted);
        editor.putString("arr_answers", jsonAnswers);
        editor.putString("arr_ques", jsonQues);
        editor.commit();

        ((MainActivity) getActivity()).AddFrag(new ResultFragment(), 1);
    }

    public void LoadTimer() {
        countDownTimer = new CountDownTimer(60000, 1000) {
            @Override
            public void onTick(long millisUntilFinished) {
                txtTimer.setText("0:" + millisUntilFinished / 1000);
            }

            @SuppressLint("SetTextI18n")
            @Override
            public void onFinish() {
                txtTimer.setText("Time Over");
            }
        };
    }

    @SuppressLint("NewApi")
    public void loadQues(boolean timer_switch) {
        try {
            id = 0;
            setBtnDefault();

            if (timer_switch) {
                LoadTimer();
                countDownTimer.start();
            }

            // 修正题号逻辑:直接根据当前count计算题号
            curr_quesNo = count + 1;
            txtStatus.setText(curr_quesNo + "/" + selectedVal);

            txtOptC.setVisibility(View.VISIBLE);
            txtOptD.setVisibility(View.VISIBLE);
            txtCode.setVisibility(View.VISIBLE);

            // 每次加载题目时重新创建布局参数,避免规则残留
            RelativeLayout.LayoutParams submit_params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            RelativeLayout.LayoutParams main_params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);

            // 重置默认布局规则
            submit_params.addRule(RelativeLayout.BELOW, mainContainer.getId());
            submit_params.topMargin = (int) convertPxToDp(getContext(), convertDpToPx(getContext(), 70));
            mainContainer.setLayoutParams(main_params);
            submitContainer.setLayoutParams(submit_params);

            // 加载题目内容
            QuestionModal currentQues = arrQues.get(count);
            txtQues.setText(currentQues.ques);
            txtOptA.setText(currentQues.optionA);
            txtOptB.setText(currentQues.optionB);
            txtOptC.setText(currentQues.optionC);
            txtOptD.setText(currentQues.optionD);

            // 安全加载code内容:先判断是否为null
            String codeContent = currentQues.code;
            if (codeContent != null) {
                txtCode.loadDataWithBaseURL(null, codeContent, "text/html", null, null);
            } else {
                txtCode.loadDataWithBaseURL(null, "", "text/html", null, null);
            }

            // 隐藏空选项(去掉空白字符判断更准确)
            if (txtOptC.getText().toString().trim().isEmpty()) txtOptC.setVisibility(View.GONE);
            if (txtOptD.getText().toString().trim().isEmpty()) txtOptD.setVisibility(View.GONE);

            // 安全判断code是否为空,避免空指针
            boolean hasCode = codeContent != null && !codeContent.trim().isEmpty();
            if (!hasCode) {
                txtCode.setVisibility(View.GONE);
            } else {
                // 修改布局规则
                submit_params.removeRule(RelativeLayout.BELOW);
                submit_params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
                submit_params.bottomMargin = (int) convertPxToDp(getContext(), convertDpToPx(getContext(), 15));
                main_params.addRule(RelativeLayout.ABOVE, submitContainer.getId());

                mainContainer.setLayoutParams(main_params);
                submitContainer.setLayoutParams(submit_params);

                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        scrollView.arrowScroll(View.FOCUS_DOWN);
                    }
                }, 1000);
            }

            ans = currentQues.answer;

            boxA.setOnClickListener(this);
            boxB.setOnClickListener(this);
            boxC.setOnClickListener(this);
            boxD.setOnClickListener(this);

        } catch (Exception e) {
            // 改为打印异常并提示,避免重新创建Fragment导致加载中断
            e.printStackTrace();
            Toast.makeText(getActivity(), "加载题目出错:" + e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onClick(View v) {
        setBtnDefault();
        id = v.getId();
        v.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
    }

    public float convertDpToPx(Context context, float dp) {
        return dp * context.getResources().getDisplayMetrics().density;
    }

    public float convertPxToDp(Context context, float px) {
        return px / context.getResources().getDisplayMetrics().density;
    }
}

额外优化点:

  • 在判断选项和code是否为空时,使用trim()去掉空白字符,避免数据库中存了空格导致判断错误。
  • 提取当前题目对象currentQues,避免多次调用arrQues.get(count),让代码更简洁高效。
  • 优化提交按钮的逻辑,把count递增的逻辑统一放在最后,避免重复代码。

内容的提问来源于stack exchange,提问作者user10555285

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:03:17