自定义视图中 child 个视图的 ID 在 Android 中相同

ID of the child views in a custom view are same in Android

我有一个自定义视图 ValueSelectorView,其中包含两个用于递增和递减值的按钮以及一个用于显示该值的 EditText。在我的 activity 中,我使用了这个视图 3 次。我在这里担心的是 child 观点,即他们的 id 在日志中检查时总是相同的。因此,结果值不正确。

我查看了 Whosebug,发现他们中的一些人遇到了同样的问题,他们正试图通过使用 setId 手动更改 child 视图的 ID 来解决这个问题。尝试使用几种不同的方法膨胀 child 视图,但问题仍然存在

这是我的自定义视图class

package eu.siacs.conversations.ui.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.annotation.RequiresApi;

import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;

public class ValueSelectorView extends RelativeLayout {

    View rootView;
    EditText valueTextView;
    View minusButton;
    View plusButton;

    private int minValue = Integer.MIN_VALUE;
    private int maxValue = Integer.MAX_VALUE;

    public ValueSelectorView(Context context) {
        super(context);
        init(context, null);
    }

    public ValueSelectorView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public ValueSelectorView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public ValueSelectorView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        rootView = LayoutInflater.from(context).inflate(R.layout.value_selector, this, true);
        Log.i(Config.LOGTAG, "****view testr**** id of this view is -->" + getResources().getResourceEntryName(rootView.getId()));
        valueTextView = rootView.findViewById(R.id.valueTextView);

        minusButton = rootView.findViewById(R.id.minusButton);
        plusButton = rootView.findViewById(R.id.plusButton);

        minusButton.setOnClickListener(v -> decrementValue());

        plusButton.setOnClickListener(v -> incrementValue());

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ValueSelectorView,0,0);
        int defaultVaue = typedArray.getInt(R.styleable.ValueSelectorView_defaultValue,0);
        Log.i(Config.LOGTAG, "****view testr**** id of this TextView is 0 -->" + valueTextView.getId());
//        Log.i(Config.LOGTAG, "****view testr**** id of this Minus Button is 0 -->" + minusButton.getId());
//        Log.i(Config.LOGTAG, "****view testr**** id of this Add Button is 0 -->" + plusButton.getId());
        valueTextView.setText(String.valueOf(defaultVaue));

        typedArray.recycle();
    }

    public void setDefaultValue(int value) {
        valueTextView.setText(String.valueOf(value));
    }

    public int getMinValue() {
        return minValue;
    }

    public void setMinValue(int minValue) {
        this.minValue = minValue;
    }

    public int getMaxValue() {
        return maxValue;
    }

    public void setMaxValue(int maxValue) {
        this.maxValue = maxValue;
    }

    public int getValue() {
        return Integer.valueOf(valueTextView.getText().toString());
    }

    /*public void setValue(int newValue) {
        int value = newValue;
        if(newValue < minValue) {
            value = minValue;
        } else if (newValue > maxValue) {
            value = maxValue;
        }
        Log.i(Config.LOGTAG, "****view testr**** id of this TextView is 1 -->" + valueTextView.getId());
        valueTextView.setText(String.valueOf(value));
    }*/

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        super.addView(child, index, params);
    }

    private void incrementValue() {

        int currentVal = Integer.valueOf(valueTextView.getText().toString());
        if(currentVal < maxValue) {
            Log.i(Config.LOGTAG, "****view testr**** id of this TextView is 2-->" + valueTextView.getId());
            valueTextView.setText(String.valueOf(currentVal + 1));
        }
    }

    private void decrementValue() {
        int currentVal = Integer.valueOf(valueTextView.getText().toString());
        if(currentVal > minValue) {
            Log.i(Config.LOGTAG, "****view testr**** id of this TextView is 3-->" + valueTextView.getId());
            valueTextView.setText(String.valueOf(currentVal - 1));
        }
    }


}

这就是我在 activity XML.

中使用它的方式
<eu.siacs.conversations.ui.widget.ValueSelectorView
                android:id="@+id/value_selector_nights"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/margin4"
                app:defaultValue="1"
                android:gravity="center_vertical"/>

<eu.siacs.conversations.ui.widget.ValueSelectorView
                    android:id="@+id/value_selector_adults"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/margin4"
                    app:defaultValue="1"
                    android:gravity="center_vertical"/>

<eu.siacs.conversations.ui.widget.ValueSelectorView
                    android:id="@+id/value_selector_children"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:defaultValue="0"
                    android:layout_marginTop="@dimen/margin4"
                    android:gravity="center_vertical"/>

以下是日志:

****view testr**** id of this view is -->value_selector_nights
****view testr**** id of this TextView is 0 -->2131297384
****view testr**** id of this view is -->value_selector_adults
****view testr**** id of this TextView is 0 -->2131297384
****view testr**** id of this view is -->value_selector_children
****view testr**** id of this TextView is 0 -->2131297384

我想了解它的内部工作原理并找到解决方案。谢谢你的时间。

对于面临同样问题的其他人。

我更新的代码是:

package eu.siacs.conversations.ui.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;

public class ValueSelectorView extends RelativeLayout {

    View rootView;
    EditText valueTextView;
    View minusButton;
    View plusButton;
    private int customState;

    private int minValue = Integer.MIN_VALUE;
    private int maxValue = Integer.MAX_VALUE;

    public ValueSelectorView(Context context) {
        super(context);
        init(context, null);
    }

    public ValueSelectorView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public ValueSelectorView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public ValueSelectorView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        rootView = LayoutInflater.from(context).inflate(R.layout.value_selector, this, true);
        Log.i(Config.LOGTAG, "****view testr**** id of this view is -->" + getResources().getResourceEntryName(rootView.getId()));
        valueTextView = rootView.findViewById(R.id.valueTextView);

        minusButton = rootView.findViewById(R.id.minusButton);
        plusButton = rootView.findViewById(R.id.plusButton);

        minusButton.setOnClickListener(v -> decrementValue());

        plusButton.setOnClickListener(v -> incrementValue());

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ValueSelectorView,0,0);
        int defaultVaue = typedArray.getInt(R.styleable.ValueSelectorView_defaultValue,0);
        Log.i(Config.LOGTAG, "****view testr**** id of this TextView is 0 -->" + valueTextView.getId());
//        Log.i(Config.LOGTAG, "****view testr**** id of this Minus Button is 0 -->" + minusButton.getId());
//        Log.i(Config.LOGTAG, "****view testr**** id of this Add Button is 0 -->" + plusButton.getId());
        valueTextView.setText(String.valueOf(defaultVaue));

        typedArray.recycle();
    }

    public void setDefaultValue(int value) {
        valueTextView.setText(String.valueOf(value));
    }

    public int getMinValue() {
        return minValue;
    }

    public void setMinValue(int minValue) {
        this.minValue = minValue;
    }

    public int getMaxValue() {
        return maxValue;
    }

    public void setMaxValue(int maxValue) {
        this.maxValue = maxValue;
    }

    public int getValue() {
        return Integer.valueOf(valueTextView.getText().toString());
    }

    /*public void setValue(int newValue) {
        int value = newValue;
        if(newValue < minValue) {
            value = minValue;
        } else if (newValue > maxValue) {
            value = maxValue;
        }
        Log.i(Config.LOGTAG, "****view testr**** id of this TextView is 1 -->" + valueTextView.getId());
        valueTextView.setText(String.valueOf(value));
    }*/

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        super.addView(child, index, params);
    }

    private void incrementValue() {

        int currentVal = Integer.valueOf(valueTextView.getText().toString());
        if(currentVal < maxValue) {
            Log.i(Config.LOGTAG, "****view testr**** id of this TextView is 2-->" + valueTextView.getId());
            valueTextView.setText(String.valueOf(currentVal + 1));
        }
    }

    private void decrementValue() {
        int currentVal = Integer.valueOf(valueTextView.getText().toString());
        if(currentVal > minValue) {
            Log.i(Config.LOGTAG, "****view testr**** id of this TextView is 3-->" + valueTextView.getId());
            valueTextView.setText(String.valueOf(currentVal - 1));
        }
    }

    public void setCustomState(int customState) {
        this.customState = customState;
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);
        ss.value = valueTextView.getText().toString();
        return ss;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        valueTextView.setText(ss.value);
    }

    public static class SavedState extends BaseSavedState {
        String value;

        SavedState(Parcelable superState) {
            super(superState);
        }

        public SavedState(Parcel source) {
            super(source);
            value = source.readString();
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeString(value);
        }

        public static final Parcelable.Creator<SavedState> CREATOR =
                new Parcelable.Creator<SavedState>() {
                    public SavedState createFromParcel(Parcel in) {
                        return new SavedState(in);
                    }

                    public SavedState[] newArray(int size) {
                        return new SavedState[size];
                    }
                };
    }
}

我的自定义视图中的子视图 XML

<EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="@dimen/medium_font"
        android:enabled="false"
        android:background="@null"
        android:textColor="#000000"
        android:layout_centerVertical="true"
        android:layout_toRightOf="@id/minusButton"
        android:saveEnabled="false"
        android:id="@+id/valueTextView" />