Код, связанный с TextWatcher, хорошо работает только с одним EditView - PullRequest
1 голос
/ 28 мая 2020

У меня следующая проблема. У меня есть действие, являющееся частью приложения приюта для животных, где пользователь должен вводить или редактировать данные, связанные с домашним животным, такие как вес, имя и порода. Я хочу показать диалоговое окно с вопросом, хочет ли пользователь продолжить редактирование или покинуть действие, в зависимости от того, действительно ли он изменил какой-либо текст в представлениях EditText. Для этого я создал логическую переменную, которая должна принимать значение true, если текст был отредактирован / запускать диалог / или оставаться false / ничего не делать / если пользователь ничего не редактировал. Я прикрепил TextWatcher к своим полям EditText и попытался изменить значение переменной на true, сделав это в onTextChanged или beforeTextChanged. Я попытался сравнить ha sh или строковые значения полей EditText с CharSequence charSequence в метод onTextChanged, но он работает только для одного из полей EditText /, что означает, что он запускает диалог, когда пользователь изменяет текст /. Каждый раз, когда я пытаюсь применить аналогичный logi c к остальным полям EditText, а также к функциональности breaks, и логическая переменная остается "истинной" независимо от того, что / означает, что пользователь видит диалог, независимо от того, изменили ли они текст или нет /. Я пробовал различные сравнения, if-logi c, оператор switch во внутреннем классе, и, похоже, ничего не работает. См. Код ниже. Спасибо.

/**
 * Allows user to create a new pet or edit an existing one.
 */
public class EditorActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>, TextWatcher {


    /**
     * EditText field to enter the pet's name
     */
    private EditText mNameEditText;

    /**
     * EditText field to enter the pet's breed
     */
    private EditText mBreedEditText;

    /**
     * EditText field to enter the pet's weight
     */
    private EditText mWeightEditText;

    /**
     * EditText field to enter the pet's gender
     */
    private Spinner mGenderSpinner;

    /**
     * Gender of the pet. The possible values are:
     * 0 for unknown gender, 1 for male, 2 for female.
     */
    public static int mGender;

    public static String mPetName;

    public static String mPetBreed;

    public static String mPetWeight;

    private static ArrayAdapter mGenderSpinnerAdapter;

    private static Uri mSinglePetUri;

    private static ContentValues mContentValues;
    // we will show warning dialog to the user,if the below variable is true
    private boolean mPetHasChanged;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_editor);
        // checks if we are about to edit the information about an existing pet or add
        // a new pet record , adjusts the activity title accordingly and initializes/
        // activates Loader only if we are updating an existing pet
        mSinglePetUri = getIntent().getData();
        if (mSinglePetUri != null) {
            setTitle(R.string.edit_pet_activity_title);
            getSupportLoaderManager().initLoader(0, null, this);
        } else {
            setTitle(getString(R.string.add_a_pet_activity_title));

        }
        // Find all relevant views that we will need to read user input from
        mNameEditText = findViewById(R.id.edit_pet_name);
        mBreedEditText = findViewById(R.id.edit_pet_breed);
        mWeightEditText = findViewById(R.id.edit_pet_weight);
        mGenderSpinner = findViewById(R.id.spinner_gender);
        setupSpinner();
        // watch for text changes
        mNameEditText.addTextChangedListener(this);
        mBreedEditText.addTextChangedListener(this);
        mWeightEditText.addTextChangedListener(this);

}


@Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

    }



  @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {


        int nameTextHashCode = mNameEditText.getText().hashCode();
        int breedTextHashCode = mBreedEditText.getText().hashCode();
        int weightTextHashCode = mWeightEditText.getText().hashCode();
        boolean nameChanged = nameTextHashCode == charSequence.hashCode();
        boolean breedChanged = breedTextHashCode == charSequence.hashCode();
        boolean weightChanged = weightTextHashCode == charSequence.hashCode();
        //this works-mPetHasChanged properly changes value
        mPetHasChanged = nameChanged ;
        //this doesn't work - the value is always true even when user didn't change a thing
        mPetHasChanged = nameChanged||breedChanged||weightChanged;
    }

    @Override
    public void afterTextChanged(Editable editable) {

    }
}

Затем в другом методе проверяется логическое значение чтобы отображать или нет диалог

if (!mPetHasChanged) {
                    NavUtils.navigateUpFromSameTask(EditorActivity.this);
                    return true;
                }

                // Otherwise if there are unsaved changes, setup a dialog to warn the user.
                // Create a click listener to handle the user confirming that
                // changes should be discarded.
                DialogInterface.OnClickListener discardButtonClickListener =
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                // User clicked "Discard" button, navigate to parent activity.
                                NavUtils.navigateUpFromSameTask(EditorActivity.this);
                            }
                        };

                // Show a dialog that notifies the user they have unsaved changes
                showUnsavedChangesDialog(discardButtonClickListener);

                return true;

XML макета:

<?xml version="1.0" encoding="utf-8"?>

<!-- Copyright (C) 2016 The Android Open Source Project
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
          http://www.apache.org/licenses/LICENSE-2.0
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<!-- Layout for the editor -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="@dimen/activity_margin"
    tools:context=".EditorActivity">

    <!-- Overview category -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <!-- Label -->
        <TextView
            android:text="@string/category_overview"
            style="@style/CategoryStyle" />

        <!-- Input fields -->
        <LinearLayout
            android:layout_height="wrap_content"
            android:layout_width="0dp"
            android:layout_weight="2"
            android:paddingLeft="4dp"
            android:orientation="vertical">

            <!-- Name field -->
            <EditText
                android:id="@+id/edit_pet_name"
                android:hint="@string/hint_pet_name"
                android:inputType="textCapWords"
                style="@style/EditorFieldStyle" />

            <!-- Breed field -->
            <EditText
                android:id="@+id/edit_pet_breed"
                android:hint="@string/hint_pet_breed"
                android:inputType="textCapWords"
                style="@style/EditorFieldStyle" />
        </LinearLayout>
    </LinearLayout>

    <!-- Gender category -->
    <LinearLayout
        android:id="@+id/container_gender"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <!-- Label -->
        <TextView
            android:text="@string/category_gender"
            style="@style/CategoryStyle" />

        <!-- Input field -->
        <LinearLayout
            android:layout_height="wrap_content"
            android:layout_width="0dp"
            android:layout_weight="2"
            android:orientation="vertical">

            <!-- Gender drop-down spinner -->
            <Spinner
                android:id="@+id/spinner_gender"
                android:layout_height="48dp"
                android:layout_width="wrap_content"
                android:paddingRight="16dp"
                android:spinnerMode="dropdown"/>
        </LinearLayout>
    </LinearLayout>

    <!-- Measurement category -->
    <LinearLayout
        android:id="@+id/container_measurement"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <!-- Label -->
        <TextView
            android:text="@string/category_measurement"
            style="@style/CategoryStyle" />

        <!-- Input fields -->
        <RelativeLayout
            android:layout_height="wrap_content"
            android:layout_width="0dp"
            android:layout_weight="2"
            android:paddingLeft="4dp">

            <!-- Weight field -->
            <EditText
                android:id="@+id/edit_pet_weight"
                android:hint="@string/hint_pet_weight"
                android:inputType="number"
                style="@style/EditorFieldStyle" />

            <!-- Units for weight (kg) -->
            <TextView
                android:id="@+id/label_weight_units"
                android:text="@string/unit_pet_weight"
                style="@style/EditorUnitsStyle"/>
        </RelativeLayout>
    </LinearLayout>
</LinearLayout>

Ответы [ 2 ]

0 голосов
/ 06 июня 2020

Это заняло у меня довольно много времени методом проб и ошибок, но мне удалось заставить код работать правильно. @LalitFauzdar - большое спасибо за то, что помогли мне найти часть решения, связанного с EditText.:

@Override
public void onTextChanged(CharSequence charSequence, int start, int before, int i2) {
    mPetHasChanged = start!= before;
}

Другая часть, как оказалось, прослушивала нажатия клавиш для удаления, когда пользователь изменяет данные. Затем мне также пришлось уведомить пользователя об ожидающих изменениях, если он изменил свой выбор в счетчике, показав пол питомца в моей деятельности. { ссылка } этот ответ дал мне идею, как отслеживать «реальные» изменения, которые пользователь внес в счетчик. Вот части кода, относящиеся к начальному вопросу:

 /**
     * Allows user to create a new pet or edit an existing one.
     */
    public class EditorActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>, TextWatcher, View.OnKeyListener, View.OnTouchListener {


        /**
         * EditText field to enter the pet's name
         */
        private EditText mNameEditText;

        /**
         * EditText field to enter the pet's breed
         */
        private EditText mBreedEditText;

        /**
         * EditText field to enter the pet's weight
         */
        private EditText mWeightEditText;

        /**
         * EditText field to enter the pet's gender
         */
        private Spinner mGenderSpinner;

        /**
         * Gender of the pet. The possible values are:
         * 0 for unknown gender, 1 for male, 2 for female.
         */
        public static int mGender;

        public static String mPetName;

        public static String mPetBreed;

        public static String mPetWeight;

        private static ArrayAdapter mGenderSpinnerAdapter;

        private static Uri mSinglePetUri;
        /**we will show warning dialog to the user,if the below variable is true*/
        private boolean mPetHasChanged;
        /**This flag turns to true if the spinner was actually touched by the user */
        private boolean spinnerActivated;


        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_editor);
            // checks if we are about to edit the information about an existing pet or add
            // a new pet record , adjusts the activity title accordingly and initializes/
            // activates Loader only if we are updating an existing pet
            mSinglePetUri = getIntent().getData();
            if (mSinglePetUri != null) {
                setTitle(R.string.edit_pet_activity_title);
                getSupportLoaderManager().initLoader(0, null, this);
            } else {
                setTitle(getString(R.string.add_a_pet_activity_title));

            }
            // Find all relevant views that we will need to read user input from
            mNameEditText = findViewById(R.id.edit_pet_name);
            mBreedEditText = findViewById(R.id.edit_pet_breed);
            mWeightEditText = findViewById(R.id.edit_pet_weight);
            mGenderSpinner = findViewById(R.id.spinner_gender);
            setupSpinner();
            mNameEditText.addTextChangedListener(this);
            mBreedEditText.addTextChangedListener(this);
            mWeightEditText.addTextChangedListener(this);
            mNameEditText.setOnKeyListener(this);
            mBreedEditText.setOnKeyListener(this);
            mWeightEditText.setOnKeyListener(this);
            mGenderSpinner.setOnTouchListener(this);



        }

/**
     * Setup the dropdown spinner that allows the user to select the gender of the pet.
     */
    private void setupSpinner() {
        // Create adapter for spinner. The list options are from the String array it will use
        // the spinner will use the default layout
        mGenderSpinnerAdapter = ArrayAdapter.createFromResource(this,
                R.array.array_gender_options, android.R.layout.simple_spinner_item);

        // Specify dropdown layout style - simple list view with 1 item per line
        mGenderSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_dropdown_item_1line);

        // Apply the adapter to the spinner
        mGenderSpinner.setAdapter(mGenderSpinnerAdapter);

        // attach listener to the spinner to handle user selection from the pet's gender list.
        mGenderSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {


                String selection = (String) parent.getItemAtPosition(position);

                if (selection.equals(getString(R.string.gender_male))) {
                    mGender = PetsEntry.PET_GENDER_MALE;



                } else if (selection.equals(getString(R.string.gender_female))) {
                    mGender = PetsEntry.PET_GENDER_FEMALE;


                } else {
                    mGender = PetsEntry.PET_GENDER_UNKNOWN;
                }
                // if the Spinner was not actually touched by the user, the 
                //spinnerActivated flag is
                //false and the method is exited earlier as there was not actual selection 
                 //change,
                //made by the user.This manages the behaviour of onItemSelected ,where the 
               //  method is
                //called twice - once when the spinner is initialized and once again if 
                // the user changes
                //selection.
                if (!spinnerActivated){
                    return;
                }
                mPetHasChanged=true;


            }


            // Because onItemSelectedListener is an interface, onNothingSelected must be 
            // defined
            @Override
            public void onNothingSelected(AdapterView<?> parent) {
                mGender = PetsEntry.PET_GENDER_UNKNOWN;
            }
        });
    }
//more code between,unrelated to topic

    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

    }
    /**The logic in this method gets triggered if the user changes existing pet's data by 
      using
     * any key except the delete one*/
    @Override
    public void onTextChanged(CharSequence charSequence, int start, int before, int count) {

        if (start != before){
            mPetHasChanged=true;
        }

    }

    @Override
    public void afterTextChanged(Editable editable) {


    }
   /** The logic in this method gets triggered if the user presses the delete button,
    *  while editing  pet's data, so we won't miss any type of editing action*/

    @Override
    public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
        if (keyCode== KeyEvent.KEYCODE_DEL){
            mPetHasChanged=true;
        }

        return false;
    }

   /**This method checks if the spinner was actually touched by the user and turns the
    * flag variable to true and based on the flag's value we trigger the further logic in
    * {#onItemSelected}*/
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        spinnerActivated=true;
        return false;
    }
}
0 голосов
/ 28 мая 2020

Когда все, что вам нужно, это mPetChanged, почему вы даже сопоставляете HashCodes?

См. Это:

@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    mPetHasChanged = !mNameEditText.getText().toString().trim().equals("") || !mBreedEditText.getText().toString().trim().equals("") || !mWeightEditText.getText().toString().trim().equals("");
}

Вы можете использовать If для каждого EditText, чтобы проверить если они пусты, но это кажется простым. Вы также можете использовать isBlank(), но он входит в StringUtils() с библиотекой Apache, вы можете использовать String.isEmpty(), который доступен в Java, но он возвращает true, если EditText содержит только пробелы.

Обновление: вы можете сравнить start и before в onTextChanged(CharSequence charSequence, int start, int before, int count), если значения start и before не совпадают, это означает, что текст изменился. Как указано ниже:

@Override
public void onTextChanged(CharSequence charSequence, int start, int before, int i2) {
    mPetHasChanged = start!= before;
}
...