【问题标题】:How to set view attribute from custom attr on a custom Preference class?如何在自定义 Preference 类上从自定义属性设置视图属性?
【发布时间】:2021-07-21 06:26:33
【问题描述】:

我创建了一个类CustomPreference,它继承自androidx Preference 类,用于设置由我自己的组件组成的自定义布局。我在该自定义 Preference 类上定义了一个可样式化的属性。但现在我试图了解如何将自定义样式属性中的值传递给我的底层SpecialCustomComponent 上的属性。

CustomPreference.java

public class CustomPreference extends Preference {

  private Context context;
  private AttributeSet attrs;

  public CustomPreference(Context context, AttributeSet attrs,
      int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    this.context = context;
    this.attrs = attrs;
    setLayoutResource(R.layout.preference_widget_custom);
  }

  // other required constructor overloads omitted

  @Override
  public void onBindViewHolder(PreferenceViewHolder holder) {
    super.onBindViewHolder(holder);
    
    // calling this method here results in an error because
    // a.getResourceId(R.styleable.PreferenceWidgetCustom_preferenceTitle, 0) 
    // returns no resource Id at this point
    setPreferenceTitle(holder);
  }

  @Override
  protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
    super.onAttachedToHierarchy(preferenceManager);
    this.preferenceManager = preferenceManager;

    // alternatively, calling this method here would result in a 
    // different error, since I don't have access to the viewHolder
    // here. To make this work, I tried preserving "viewHolder" in a 
    // field inside onBindViewHolder (which I don't think I'm 
    // supposed to do anyways), but the viewHolder reference was 
    // null, so it didn't work.
    setPreferenceTitle();
  }

  // This is my incorrect attempt at setting "myText" to the value of "preferenceTitle"
  private void setPreferenceTitle(PreferenceViewHolder holder) {
    TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.PreferenceWidgetSwitchCustom, 0, 0);

    try {
      int preferenceTitleId = a
          .getResourceId(R.styleable.PreferenceWidgetCustom_preferenceTitle, 0);

      ((SpecialCustomComponent) holder.itemView).setText(a.getResources().getString(preferenceTitleId));
    }
    finally {
      a.recycle();
    }
  }
}

我在 attrs.xml 中为我的 CustomPreference 声明了一个自定义属性,我希望使用该属性在布局中设置我的自定义组件的底层“myText”属性。

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="PreferenceWidgetCustom">
    <attr name="preferenceTitle" format="string" />
  </declare-styleable>
</resources>

每个CustomPreference 的布局在preference_widget_custom.xml 中定义。注意属性“myText”,我想将其设置为上面定义的“preferenceTitle”的值。

preference_widget_custom.xml

<?xml version="1.0" encoding="utf-8"?>
<SpecialCustomComponent
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:myText="this is the field I would like to be set by 'preferenceTitle'" />

我在 PreferenceScreen 布局中使用这些首选项,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:title="Settings">
    <CustomPreference
        android:key="pref_one"
        app:preferenceTitle="Preference #1"/>
    <CustomPreference
        android:key="pref_two"
        app:preferenceTitle="Preference #2"/>
</PreferenceScreen>

【问题讨论】:

    标签: android android-preferences


    【解决方案1】:

    string 资源作为styleable 提供通常是错误的,这就是为什么您将简单的本地化工作过度复杂化的原因。可以使用此模式访问任何资源:[&lt;package_name&gt;.]R.&lt;resource_type&gt;.&lt;resource_name&gt;

    所以这将是res/values/strings.xml:

    <resources>
        <string name="title_preference_01">Title 01</string>
        <string name="summary_preference_01">Summary 01</string>
        <string name="title_preference_02">Title 02</string>
        <string name="summary_preference_02">Summary 02</string>
    <resources>
    

    然后可以通过应用程序访问:

    <?xml version="1.0" encoding="utf-8"?>
    <PreferenceScreen
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:title="Settings">
    
        <CustomPreference
            android:title="@string/title_preference_01"
            android:summary="@string/summary_preference_01"
            android:key="pref_one" />
    
        <CustomPreference
            android:title="@string/title_preference_02"
            android:summary="@string/summary_preference_02"
            android:key="pref_two" />
    
    </PreferenceScreen>
    

    Localize your app 详细解释了这一切。


    此外,即使它是实际可样式化的值而不是通常用于本地化的字符串,您仍然需要将 PreferenceWidgetCustom 样式应用于 SpecialCustomComponent 才能访问它的值。

    【讨论】:

    • 我不确定这个解决方案是否适合我的问题。由于我使用 CustomPreference 的自定义布局,我需要以某种方式设置 SpecialCustomComponent 的“myText”字段。设置方式,在 CustomPreference 上设置 'title' 不会有任何好处,因为底层布局只显示 'myText' 字段中的内容。有没有办法从 CustomPreference 类访问“android:title”的值并使用它来设置 SpecialCustomComponent 的“myText”字段? (还要提一下,SpecialCustomComponent 不是我的,所以不能改)
    • 感谢您指出此处使用可样式化属性是不正确的。已注明。
    • 这种风格化方法的唯一问题是,styleable 不能像 string 一样被本地化——这在只支持一种语言时可能会被忽视,但应该很明显如何自我击败它是,当试图添加另一种语言时。尽管它严重滥用了框架,但这种误解可能源于将该组件视为给定的。建议从头开始。
    • 是的,好点。我可以轻松地交换使用字符串资源的解决方案。但是我仍在尝试解决我的问题,即根据 CustomPreference 的属性值(如您所指出的那样,可以是 android:title 而不是 styleable)为 SpecialCustomComponent (app:myText) 设置属性。如果您对此有任何建议,请告诉我。
    【解决方案2】:

    我解决了我的问题。正如 Martin 指出的那样,styleable 无论如何都不是真正用于字符串的。事实证明,使用内置的 android:title 属性而不是自定义样式让事情变得非常简单。我所要做的就是调用this.getTitle() 从我的CustomPreference 类中访问它的值。

    public class CustomPreference extends Preference {
    
      public CustomPreference(Context context, AttributeSet attrs,
          int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        setLayoutResource(R.layout.preference_widget_custom);
      }
    
      // other required constructor overloads omitted
    
      @Override
      public void onBindViewHolder(PreferenceViewHolder holder) {
        super.onBindViewHolder(holder);
        setPreferenceTitle();
      }
    
      private void setPreferenceTitle() {
          ((SpecialCustomComponent) holder.itemView).setText(this.getTitle());
      }
    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-18
      • 1970-01-01
      相关资源
      最近更新 更多