Android:是否可以为自定义视图“公开”子视图属性?

问题描述

我正在尝试为我创建的自定义视图“公开”子视图的属性。在 XML 中,这看起来像:

<com.isupatches.android.customview.MyView
    android:id="@+id/myView"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:textColor="@android:color/holo_green_light" <-- This is from AppCompatTextView
    />

支持类看起来像:

class MyView @JvmOverloads constructor(
    context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0
) : FrameLayout(context,attrs,defStyleAttr) {

    private val impl: AppCompatTextView = AppCompatTextView(context,defStyleAttr)

    var text: String? = null
        set(value) {
            field = value
            impl.text = value
        }

    init {
        addView(impl)
    }
}

我注意到 android:textColor 应用得当,但我破坏了 IDE 支持

是否支持像这样从另一个 <declared-stylable> 继承:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyView" parent="AppCompatTextView" />
</resources>

这样我就不必为了 IDE 支持而重复所有引用?如果我重复,它看起来像这样:

```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyView">
        <!-- Determines the minimum type that getText() will return.
             The default is "normal".
             Note that EditText and LogTextBox always return Editable,even if you specify something less powerful here. -->
        <attr name="bufferType">
            <!-- Can return any CharSequence,possibly a
             Spanned one if the source text was Spanned. -->
            <enum name="normal" value="0" />
            <!-- Can only return Spannable. -->
            <enum name="spannable" value="1" />
            <!-- Can only return Spannable and Editable. -->
            <enum name="editable" value="2" />
        </attr>
        <!-- Text to display. -->
        <attr name="text" format="string" localization="suggested" />
        <!-- Hint text to display when the text is empty. -->
        <attr name="hint" format="string" />
        <!-- Text color. -->
        <attr name="textColor" />
        <!-- Color of the text selection highlight. -->
        <attr name="textColorHighlight" />
        <!-- Color of the hint text. -->
        <attr name="textColorHint" />
        <!-- Base text color,typeface,size,and style. -->
        <attr name="textAppearance" />
        <!-- Size of the text. Recommended dimension type for text is "sp" for scaled-pixels (example: 15sp). -->
        <attr name="textSize" />
        <!-- Sets the horizontal scaling factor for the text. -->
        <attr name="textScaleX" format="float" />
        <!-- Typeface (normal,sans,serif,monospace) for the text. -->
        <attr name="typeface" />
        <!-- Style (normal,bold,italic,bold|italic) for the text. -->
        <attr name="textStyle" />
        <!-- Weight for the font used in the TextView. -->
        <attr name="textFontWeight" format="integer"/>
        <!-- Font family (named by string or as a font resource reference) for the text. -->
        <attr name="fontFamily" />
        <!-- Specifies the {@link android.os.LocaleList} for the text in this TextView.
             If not given,the system default will be used.
             May be a string value,which is a comma-separated language tag list,such as "ja-JP,zh-CN".
             When not specified or an empty string is given,it will fallback to the default one.
             {@see android.os.LocaleList#forLanguageTags(String)}
             {@see android.widget.TextView#setTextLocales(android.os.LocaleList)} -->
        <attr name="textLocale" format="string" />
        <!-- Text color for links. -->
        <attr name="textColorLink" />
        <!-- Makes the cursor visible (the default) or invisible. -->
        <attr name="cursorVisible" format="boolean" />
        <!-- Makes the TextView be at most this many lines tall.

        When used on an editable text,the <code>inputType</code> attribute's value must be
        combined with the <code>textMultiLine</code> flag for the maxLines attribute to apply. -->
        <attr name="maxLines" format="integer" min="0" />
        <!-- Makes the TextView be at most this many pixels tall. -->
        <attr name="maxHeight" />
        <!-- Makes the TextView be exactly this many lines tall. -->
        <attr name="lines" format="integer" min="0" />
        <!-- Makes the TextView be exactly this tall.
             You Could get the same effect by specifying this number in the
             layout parameters. -->
        <attr name="height" format="dimension" />
        <!-- Makes the TextView be at least this many lines tall.

        When used on an editable text,the <code>inputType</code> attribute's value must be
        combined with the <code>textMultiLine</code> flag for the minLines attribute to apply. -->
        <attr name="minLines" format="integer" min="0" />
        <!-- Makes the TextView be at least this many pixels tall. -->
        <attr name="minHeight" />
        <!-- Makes the TextView be at most this many ems wide. -->
        <attr name="maxEms" format="integer" min="0" />
        <!-- Makes the TextView be at most this many pixels wide. -->
        <attr name="maxWidth" />
        <!-- Makes the TextView be exactly this many ems wide. -->
        <attr name="ems" format="integer" min="0" />
        <!-- Makes the TextView be exactly this wide.
             You Could get the same effect by specifying this number in the
             layout parameters. -->
        <attr name="width" format="dimension" />
        <!-- Makes the TextView be at least this many ems wide. -->
        <attr name="minems" format="integer" min="0" />
        <!-- Makes the TextView be at least this many pixels wide. -->
        <attr name="minWidth" />
        <!-- Specifies how to align the text by the view's x- and/or y-axis
             when the text is smaller than the view. -->
        <attr name="gravity" />
        <!-- Whether the text is allowed to be wider than the view (and
             therefore can be scrolled horizontally). -->
        <attr name="scrollHorizontally" format="boolean" />
        <!-- Whether the characters of the field are displayed as
             password dots instead of themselves.
             {@deprecated Use inputType instead.} -->
        <attr name="password" format="boolean" />
        <!-- Constrains the text to a single horizontally scrolling line
             instead of letting it wrap onto multiple lines,and advances
             focus instead of inserting a newline when you press the
             enter key.

             The default value is false (multi-line wrapped text mode) for non-editable text,but if
             you specify any value for inputType,the default is true (single-line input field mode).

             {@deprecated This attribute is deprecated. Use <code>maxLines</code> instead to change
             the layout of a static text,and use the <code>textMultiLine</code> flag in the
             inputType attribute instead for editable text views (if both singleLine and inputType
             are supplied,the inputType flags will override the value of singleLine). } -->
        <attr name="singleLine" format="boolean" />
        <!-- Specifies whether the widget is enabled. The interpretation of the enabled state varies by subclass.
             For example,a non-enabled EditText prevents the user from editing the contained text,and
             a non-enabled Button prevents the user from tapping the button.
             The appearance of enabled and non-enabled widgets may differ,if the drawables referenced
             from evaluating state_enabled differ. -->
        <attr name="enabled" format="boolean" />
        <!-- If the text is selectable,select it all when the view takes
             focus. -->
        <attr name="selectAllOnFocus" format="boolean" />
        <!-- Leave enough room for ascenders and descenders instead of
             using the font ascent and descent strictly.  (normally true). -->
        <attr name="includeFontPadding" format="boolean" />
        <!-- Set an input filter to constrain the text length to the
             specified number. -->
        <attr name="maxLength" format="integer" min="0" />
        <!-- Place a blurred shadow of text underneath the text,drawn with the
             specified color. The text shadow produced does not interact with
             properties on View that are responsible for real time shadows,{@link android.R.styleable#View_elevation elevation} and
             {@link android.R.styleable#View_translationZ translationZ}. -->
        <attr name="shadowColor" />
        <!-- Horizontal offset of the text shadow. -->
        <attr name="shadowDx" />
        <!-- Vertical offset of the text shadow. -->
        <attr name="shadowDy" />
        <!-- Blur radius of the text shadow. -->
        <attr name="shadowRadius" />
        <attr name="autoLink" />
        <!-- If set to false,keeps the movement method from being set
             to the link movement method even if autoLink causes links
             to be found. -->
        <attr name="linksClickable" format="boolean" />
        <!-- If set,specifies that this TextView has a numeric input method.
             The default is false.
             {@deprecated Use inputType instead.} -->
        <attr name="numeric">
            <!-- Input is numeric. -->
            <flag name="integer" value="0x01" />
            <!-- Input is numeric,with sign allowed. -->
            <flag name="signed" value="0x03" />
            <!-- Input is numeric,with decimals allowed. -->
            <flag name="decimal" value="0x05" />
        </attr>
        <!-- If set,specifies that this TextView has a numeric input method
             and that these specific characters are the ones that it will
             accept.
             If this is set,numeric is implied to be true.
             The default is false. -->
        <attr name="digits" format="string" />
        <!-- If set,specifies that this TextView has a phone number input
             method. The default is false.
             {@deprecated Use inputType instead.} -->
        <attr name="phoneNumber" format="boolean" />
        <!-- If set,specifies that this TextView should use the specified
             input method (specified by fully-qualified class name).
             {@deprecated Use inputType instead.} -->
        <attr name="inputMethod" format="string" />
        <!-- If set,specifies that this TextView has a textual input method
             and should automatically capitalize what the user types.
             The default is "none".
             {@deprecated Use inputType instead.} -->
        <attr name="capitalize">
            <!-- Don't automatically capitalize anything. -->
            <enum name="none" value="0" />
            <!-- Capitalize the first word of each sentence. -->
            <enum name="sentences" value="1" />
            <!-- Capitalize the first letter of every word. -->
            <enum name="words" value="2" />
            <!-- Capitalize every character. -->
            <enum name="characters" value="3" />
        </attr>
        <!-- If set,specifies that this TextView has a textual input method
             and automatically corrects some common spelling errors.
             The default is "false".
             {@deprecated Use inputType instead.} -->
        <attr name="autoText" format="boolean" />
        <!-- If set,specifies that this TextView has an input method.
             It will be a textual one unless it has otherwise been specified.
             For TextView,this is false by default.  For EditText,it is
             true by default.
             {@deprecated Use inputType instead.} -->
        <attr name="editable" format="boolean" />
        <!-- If set,the text view will include its current complete text
             inside of its frozen icicle in addition to Meta-data such as
             the current cursor position.  By default this is disabled;
             it can be useful when the contents of a text view is not stored
             in a persistent place such as a content provider. For
             {@link android.widget.EditText} it is always enabled,regardless
             of the value of the attribute. -->
        <attr name="freezesText" format="boolean" />
        <!-- If set,causes words that are longer than the view is wide
             to be ellipsized instead of broken in the middle.
             You will often also want to set scrollHorizontally or singleLine
             as well so that the text as a whole is also constrained to
             a single line instead of still allowed to be broken onto
             multiple lines. -->
        <attr name="ellipsize" />
        <!-- The drawable to be drawn above the text. -->
        <attr name="drawabletop" format="reference|color" />
        <!-- The drawable to be drawn below the text. -->
        <attr name="drawableBottom" format="reference|color" />
        <!-- The drawable to be drawn to the left of the text. -->
        <attr name="drawableLeft" format="reference|color" />
        <!-- The drawable to be drawn to the right of the text. -->
        <attr name="drawableRight" format="reference|color" />
        <!-- The drawable to be drawn to the start of the text. -->
        <attr name="drawableStart" format="reference|color" />
        <!-- The drawable to be drawn to the end of the text. -->
        <attr name="drawableEnd" format="reference|color" />
        <!-- The padding between the drawables and the text. -->
        <attr name="drawablePadding" format="dimension" />
        <!-- Tint to apply to the compound (left,top,etc.) drawables. -->
        <attr name="drawableTint" format="color" />
        <!-- Blending mode used to apply the compound (left,etc.) drawables tint. -->
        <attr name="drawableTintMode">
            <!-- The tint is drawn on top of the drawable.
                 [Sa + (1 - Sa)*Da,Rc = Sc + (1 - Sa)*Dc] -->
            <enum name="src_over" value="3" />
            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
                 color channels are thrown out. [Sa * Da,Sc * Da] -->
            <enum name="src_in" value="5" />
            <!-- The tint is drawn above the drawable,but with the drawable’s alpha
                 channel masking the result. [Da,Sc * Da + (1 - Sa) * Dc] -->
            <enum name="src_atop" value="9" />
            <!-- Multiplies the color and alpha channels of the drawable with those of
                 the tint. [Sa * Da,Sc * Dc] -->
            <enum name="multiply" value="14" />
            <!-- [Sa + Da - Sa * Da,Sc + Dc - Sc * Dc] -->
            <enum name="screen" value="15" />
            <!-- Combines the tint and drawable color and alpha channels,clamping the
                 result to valid color values. Saturate(S + D) -->
            <enum name="add" value="16" />
        </attr>
        <!-- Extra spacing between lines of text. The value will not be applied for the last
             line of text. -->
        <attr name="linespacingExtra" format="dimension" />
        <!-- Extra spacing between lines of text,as a multiplier. The value will not be applied
             for the last line of text.-->
        <attr name="linespacingMultiplier" format="float" />
        <!-- Explicit height between lines of text. If set,this will override the values set
             for linespacingExtra and linespacingMultiplier. -->
        <attr name="lineHeight" format="dimension" />
        <!-- distance from the top of the TextView to the first text baseline. If set,this
             overrides the value set for paddingTop. -->
        <attr name="firstBaselinetoTopHeight" format="dimension" />
        <!-- distance from the bottom of the TextView to the last text baseline. If set,this
             overrides the value set for paddingBottom. -->
        <attr name="lastBaselinetoBottomHeight" format="dimension" />
        <!-- The number of times to repeat the marquee animation. Only applied if the
             TextView has marquee enabled. -->
        <attr name="marqueeRepeatLimit" format="integer">
            <!-- Indicates that marquee should repeat indefinitely. -->
            <enum name="marquee_forever" value="-1" />
        </attr>
        <attr name="inputType" />
        <!-- Whether undo should be allowed for editable text. Defaults to true. -->
        <attr name="allowUndo" format="boolean" />
        <attr name="imeOptions" />
        <!-- An addition content type description to supply to the input
             method attached to the text view,which is private to the
             implementation of the input method.  This simply fills in
             the {@link android.view.inputmethod.EditorInfo#privateImeOptions
             EditorInfo.privateImeOptions} field when the input
             method is connected. -->
        <attr name="privateImeOptions" format="string" />
        <!-- Supply a value for
             {@link android.view.inputmethod.EditorInfo#actionLabel EditorInfo.actionLabel}
             used when an input method is connected to the text view. -->
        <attr name="imeActionLabel" format="string" />
        <!-- Supply a value for
             {@link android.view.inputmethod.EditorInfo#actionId EditorInfo.actionId}
             used when an input method is connected to the text view. -->
        <attr name="imeActionId" format="integer" />
        <!-- Reference to an
             {@link android.R.styleable#InputExtras &lt;input-extras&gt;}
             XML resource containing additional data to
             supply to an input method,which is private to the implementation
             of the input method.  This simply fills in
             the {@link android.view.inputmethod.EditorInfo#extras
             EditorInfo.extras} field when the input
             method is connected. -->
        <attr name="editorExtras" format="reference" />

        <!-- Reference to a drawable that will be used to display a text selection
             anchor on the left side of a selection region. -->
        <attr name="textSelectHandleLeft" />
        <!-- Reference to a drawable that will be used to display a text selection
             anchor on the right side of a selection region. -->
        <attr name="textSelectHandleRight" />
        <!-- Reference to a drawable that will be used to display a text selection
             anchor for positioning the cursor within text. -->
        <attr name="textSelectHandle" />
        <!-- The layout of the view that is displayed on top of the cursor to paste inside a
             TextEdit field. -->
        <attr name="textEditPasteWindowLayout" />
        <!-- Variation of textEditPasteWindowLayout displayed when the clipboard is empty. -->
        <attr name="textEditnopasteWindowLayout" />
        <!-- Used instead of textEditPasteWindowLayout when the window is moved on the side of the
             insertion cursor because it would be clipped if it were positioned on top. -->
        <attr name="textEditSidePasteWindowLayout" />
        <!-- Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. -->
        <attr name="textEditSidenopasteWindowLayout" />

        <!-- Layout of the TextView item that will populate the suggestion popup window. -->
        <attr name="textEditSuggestionItemLayout" />
        <!-- Layout of the container of the suggestion popup window. -->
        <attr name="textEditSuggestionContainerLayout" />
        <!-- Style of the highlighted string in the suggestion popup window. -->
        <attr name="textEditSuggestionHighlightStyle" />


        <!-- Reference to a drawable that will be drawn under the insertion cursor. -->
        <attr name="textCursorDrawable" />

        <!-- Indicates that the content of a non-editable text can be selected. -->
        <attr name="textIsSelectable" />
        <!-- Present the text in ALL CAPS. This may use a small-caps form when available. -->
        <attr name="textAllCaps" />
        <!-- Elegant text height,especially for less compacted complex script text. -->
        <attr name="elegantTextHeight" />
        <!-- Whether to respect the ascent and descent of the fallback fonts that are used in
        displaying the text. When true,fallback fonts that end up getting used can increase
        the ascent and descent of the lines that they are used on. -->
        <attr name="fallbackLinespacing" format="boolean"/>
        <!-- Text letter-spacing. -->
        <attr name="letterSpacing" />
        <!-- Font feature settings. -->
        <attr name="fontFeatureSettings" />
        <!-- Font variation settings. -->
        <attr name="fontvariationSettings" />
        <!-- Break strategy (control over paragraph layout). -->
        <attr name="breakStrategy">
            <!-- Line breaking uses simple strategy. -->
            <enum name="simple" value="0" />
            <!-- Line breaking uses high-quality strategy,including hyphenation. -->
            <enum name="high_quality" value="1" />
            <!-- Line breaking strategy balances line lengths. -->
            <enum name="balanced" value="2" />
        </attr>
        <!-- Frequency of automatic hyphenation. -->
        <attr name="hyphenationFrequency">
            <!-- No hyphenation. -->
            <enum name="none" value="0" />
            <!-- Less frequent hyphenation,useful for informal use cases,such
            as chat messages. -->
            <enum name="normal" value="1" />
            <!-- Standard amount of hyphenation,useful for running text and for
            screens with limited space for text. -->
            <enum name="full" value="2" />
        </attr>
        <!-- Specify the type of auto-size. Note that this feature is not supported by EditText,works only for TextView. -->
        <attr name="autoSizeTextType" format="enum">
            <!-- No auto-sizing (default). -->
            <enum name="none" value="0" />
            <!-- Uniform horizontal and vertical text size scaling to fit within the
            container. -->
            <enum name="uniform" value="1" />
        </attr>
        <!-- Specify the auto-size step size if <code>autoSizeTextType</code> is set to
        <code>uniform</code>. The default is 1px. Overwrites
        <code>autoSizePresetSizes</code> if set. -->
        <attr name="autoSizeStepGranularity" format="dimension" />
        <!-- Resource array of dimensions to be used in conjunction with
        <code>autoSizeTextType</code> set to <code>uniform</code>. Overrides
        <code>autoSizeStepGranularity</code> if set. -->
        <attr name="autoSizePresetSizes"/>
        <!-- The minimum text size constraint to be used when auto-sizing text. -->
        <attr name="autoSizeMinTextSize" format="dimension" />
        <!-- The maximum text size constraint to be used when auto-sizing text. -->
        <attr name="autoSizeMaxTextSize" format="dimension" />
        <!-- Mode for justification. -->
        <attr name="justificationMode">
            <!-- No justification. -->
            <enum name="none" value="0" />
            <!-- Justification by stretching word spacing. -->
            <enum name="inter_word" value = "1" />
        </attr>
</resources>

并且如果 TextView 添加/删除或更改属性,则很容易不同步。

解决方法

您应该能够在您自己的样式化 XML 文件中为 bufferType、text 和 textSize 引用 Android 属性,如下所示:

<resources>
    <declare-styleable name="MyView">
        <attr name="android:bufferType" />
        <attr name="android:text" />
        <attr name="android:textSize" />
    </declare-styleable>
</resources>

您可以根据需要添加其他人。如果 TextView 属性发生变化,您将需要对文件进行更改。

enter image description here