【问题标题】:Inflate into "this"?膨胀成“这个”?
【发布时间】:2014-03-20 04:00:39
【问题描述】:

我一直在创建没有太多 XML 的应用程序,以编程方式创建视图。我想切换到 XML。所以我为RelativeLayout写了一个XML文件,我需要把它膨胀成一个现有的类(当然是RelativeLayout的一个子类),它有所有的实现逻辑。

如何在构造函数中膨胀成“this”?

顺便问一下,XML 的真正优势是什么?当我在代码中创建视图时,我缩放字体和图像,并根据屏幕的大小、方向、纵横比等移动视图。使用 XML 方法,我必须为所有可能的配置创建一个单独的 XML。 .

构造函数代码:

  public OrderEditControl()
  {
    super(LmcActivity.W.getApplicationContext());
    Resources res = LmcActivity.W.getResources();
    setBackgroundColor(Color.TRANSPARENT);
    headers = res.getStringArray(R.array.item_list_columns);
    widths = new int[headers.length];

    createLabels();
    createButtons();

    LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
    lp.addRule(ALIGN_PARENT_TOP);
    lp.addRule(RIGHT_OF, labels[LabelType.CUSTOMER.ordinal()].getId());
    lp.addRule(LEFT_OF, buttons[ButtonType.FIND_CUSTOMER.ordinal()].getId());

    customerView = new TextView(LmcActivity.W.getApplicationContext());
    customerView.setTextColor(Color.BLACK);
    customerView.setId(400);
    customerView.setTypeface(Typeface.DEFAULT_BOLD);
    customerView.setGravity(Gravity.CENTER_VERTICAL);
    addView(customerView, lp);

    lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    lp.addRule(ALIGN_TOP, labels[LabelType.SHIP_TYPE.ordinal()].getId());
    lp.addRule(ALIGN_BOTTOM, labels[LabelType.SHIP_TYPE.ordinal()].getId());
    lp.addRule(RIGHT_OF, labels[LabelType.SHIP_TYPE.ordinal()].getId());

    shipSpinner = new Spinner(LmcActivity.W);
    shipSpinner.setId(401);
    shipSpinner.setAdapter(shipAdapter);
    shipSpinner.setOnItemSelectedListener(this);
    addView(shipSpinner, lp);

    deliveryView = new EditText(LmcActivity.W.getApplicationContext());
    deliveryView.setGravity(Gravity.CENTER_VERTICAL);
    deliveryView.setSingleLine();
    deliveryView.setId(402);
    addView(deliveryView);

    lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
    lp.addRule(RIGHT_OF, labels[LabelType.COMMENTS.ordinal()].getId());
    lp.addRule(LEFT_OF, buttons[ButtonType.ITEMS.ordinal()].getId());
    lp.addRule(ALIGN_TOP, labels[LabelType.COMMENTS.ordinal()].getId());

    commentView = new EditText(LmcActivity.W.getApplicationContext());
    commentView.setGravity(Gravity.TOP);
    commentView.setId(403);
    addView(commentView, lp);

    lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
    lp.addRule(BELOW, commentView.getId());
    itemList = new ListView(LmcActivity.W.getApplicationContext());
    itemList.addHeaderView(createRow(null, null), null, false);
    itemList.setOnItemClickListener(this);
    itemList.setAdapter(itemAdapter);
    itemList.setCacheColorHint(0);
    itemList.setBackgroundColor(Color.TRANSPARENT);
    itemList.setId(404);
    addView(itemList, lp);

    lays[0] = new LayParm(false);
    lays[1] = new LayParm(true);
  }

  /** create the view's buttons */
  private void createButtons()
  {
    for (int i = 0; i < N_BUT; ++i)
    {
      Button but = i == ButtonType.ITEMS.ordinal() ?
          new TextGlassButton(2.4f, LmcActivity.W.getResources().getString(R.string.items), Color.WHITE) :
          new EffGlassButton(1.2f, butEffects[i]);
      but.setId(BUT_ID + i);
      but.setOnClickListener(this);
      buttons[i] = but;

      if (i == ButtonType.DATE.ordinal())
        addView(but);
      else
      {
        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        if (i < 2)
          lp.addRule(ALIGN_PARENT_TOP);
        else
          lp.addRule(BELOW, BUT_ID + i - 2);

        if (i % 2 == 0)
          lp.addRule(ALIGN_PARENT_RIGHT);
        else
          lp.addRule(LEFT_OF, BUT_ID + i - 1);

        addView(but, lp);
      }
    }
  }

  /** create text labels */
  private void createLabels()
  {
    Paint paint = AFDraw.W.textPaint;
    paint.setTextSize(Universe.TEXT_SIZE);
    paint.setTypeface(LmcActivity.W.defaultTypeface);

    String[] titles = LmcActivity.W.getResources().getStringArray(R.array.order_labels);

    for (int i = 0; i < titles.length; ++i)
    {
      LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
      lp.addRule(ALIGN_PARENT_LEFT);

      if (i == 0)
        lp.addRule(ALIGN_PARENT_TOP);
      else
        lp.addRule(BELOW, LABEL_ID + i - 1);

      TextView tv = new TextView(LmcActivity.W.getApplicationContext());
      tv.setText(titles[i]);
      tv.setTextColor(Color.BLACK);
      tv.setId(LABEL_ID + i);
      tv.setTypeface(LmcActivity.W.defaultTypeface);
      tv.setGravity(Gravity.CENTER_VERTICAL);
      labels[i] = tv;
      addView(tv, lp);

      labelWidth = Math.max(labelWidth, paint.measureText(titles[i]));
    }

    labelWidth += Universe.TEXT_SIZE * 0.5f;
    dateWidth = paint.measureText("00/00/00") + Universe.TEXT_SIZE * 1.5f;
  }

【问题讨论】:

  • 你能提供你的代码吗?
  • 我不确定您需要什么代码...好的,我放入了我想用 inflate() 替换的构造函数。
  • 请发布您的代码

标签: android xml android-inflate


【解决方案1】:

@scriptocalypse 通常是正确的,但是将一些布局子类化并将自定义布局膨胀到此类有助于分离不同的抽象。有很多糟糕的教程,其中一切都在Activity中完成。我看到世界上新的 comming 程序员只会编写看起来很垃圾的应用程序。

使用自定义布局,您只能在 Activity 中做这样的事情:

medicineView.putMedicine(medicineList);

而不是所有糟糕的适配器创建和寻找视图......

首先您应该为您的自定义视图创建一些视图:

<RelativeLayout ...>
    <!-- You put all your views here -->
</RelativeLayout>

其次如果你对你的观点满意,你应该把root改为merge标签:

<merge ...>
    <!-- You put all your views here -->
</merge>

这是非常重要的。我们从 RelativeLayout 标签开始设计,以便 IDE 知道如何绘制布局以及如何完成。但是如果我们保持原样,我们最终会出现在两个嵌套的 RelativeLayouts 中,最终会是这样的:

<RelativeLayout ...>    <!-- That is your class -->
    <RelativeLayout ...> <!-- This is inflated from layout -->  
        <!-- You put all your views here -->
    </RelativeLayout>
</RelativeLayout>

如果您将布局更改为“合并”,那么它将如下所示:

<RelativeLayout ...>    <!-- That is your class -->
    <merge...> <!-- This is inflated from layout -->  
        <!-- You put all your views here -->
    </merge>
</RelativeLayout>

并将被合并到它的根目录:

<RelativeLayout ...>    <!-- That is your class, merged with layout -->
    <!-- You put all your views here -->
</RelativeLayout>

最后你必须继承要求的 View 或 ViewGroup:

public class CustomView extends RelativeLayout {
    public CustomView(Context context) {
        super(context);
        initialize();
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }

    public CustomView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initialize();
    }

    private void initialize() {
        LayoutInflater inflater = LayoutInflater.from(getContext());
        inflater.inflate(R.id.your_layout, this, true);

        // find your views, set handlers etc.
    }
}

用法

就像@scriptocalypse 已经说过的那样。在另一个布局中,您可以这样使用:

<SomeLayout>
    <com.foo.CustomView>
</SomeLayout>

【讨论】:

  • 酷——所以它是“合并”!一个相关的问题,也解释了为什么我发现在代码中创建布局更简单:我选择标准“按钮大小”为min(screen_size_in_pix/C1, pix_in_inch/C2),然后选择标准字体大小为button_size/C3——并从那里绘制内容。一些视图使用更大或更小的字体,即font_size * C4。当然,Ci 都是不变的。这会在不同的屏幕尺寸上产生非常好的和可靠的结果。我可以在 XML 中做同样的事情吗?
【解决方案2】:

首先,回答您的主要问题:

您不希望将 XML RelativeLayout 膨胀到您的 RelativeLayout 类中。您将扩展 RelativeLayout,然后在 XML 文件中声明您的 RelativeLayout 实例,如下所示:

// com.foo.MyRelativeLayout.java
public class MyRelativeLayout extends RelativeLayout{
    /**
     * Implement MyRelativeLayout
     */
}

还有……

// layout_example.xml
<?xml version="1.0" encoding="utf-8"?>
<com.foo.MyRelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <!-- Put More Views in here... -->
    <TextView
       android:id="@+id/customer_textview"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/customer_name_placeholder" />
    <!-- and on... -->

</com.foo.MyRelativeLayout>

但更重要的是,如果您使用 XML 来布置文件,则不需要在 MyRelativeLayout 文件中进行 任何 实例化或 .addRule() 方法调用,因为你已经在 XML 中以声明方式完成了。

回答您的第二个问题“为什么还要使用 XML?”

有很多原因。也许这些适用于你,也许它们不适用,但它们是我可以很容易想到并与我的工作相关的那些。

  • 您实际上不必为每个单独的屏幕尺寸或用例创建一个新的布局文件。在大多数情况下,一个布局文件就足以满足大多数屏幕的需求。您可能会发现您将拥有特定于大小/分辨率/方向的 dimens.xml 或 style.xml 文件,但除非您希望针对不同的可能性进行截然不同的安排,否则布局本身不会经常重复。

  • 您可以使用可视化编辑器。如果您在团队中工作,并且您的队友不喜欢或只想使用 Java 来布置他们的屏幕,这一点很重要。虽然我和其他人很乐意创建 View 和 Layout 子类来满足我们的需求,但我知道确实没有人更喜欢使用 Java 作为他们的主要布局语言。寻找与您合作的人(或其他人都使用 XML 工具的工作)可能具有挑战性。

  • 如果您正在创建供其他人使用的工具(例如上述喜欢 XML 的人),您实际上可以为他们提供自定义属性以使用,这使得定位和布局更加强大。这些属性可以在 XML 中进行硬编码,或者它们可以是对任何其他 Android 资源(drawable/string/color/integer/boolean/etc...)的引用。作为一个人为的示例,但基于您的代码,您可以让您的用户能够指定要创建的按钮数量,而不是依赖于 N_BUT 变量。您可以给它一个默认值,但为用户提供一种在 XML 中更改它的方法。

这是一个例子:

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

 <com.foo.MyRelativeLayout
     xmlns:param="http://schemas.android.com/apk/res-auto"
     style="@style/MyRelativeLayoutStyle"
     param:numberOfButtons="3">

 </com.foo.MyRelativeLayout>

在不同的文件中...

//attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyRelativeLayout">    
        <attr name="numberOfButtons" format="reference|integer" />
    </declare-styleable>
</resources>

在您的 MyRelativeLayout 中,您可以在其构造函数(Android 使用 XML 创建布局时调用的那个)中的 AttributeSet 访问这些属性。

  • 使用 style="@style/foo" 语法可以让您创建可应用于各种视图的样式“类”,而无需实际创建视图子类。假设您知道您总是希望有一组参数适用于所有 Button 元素,但不想继承 Button。

例如:

// styles.xml
 <style name="BaseButton">
     <item name="android:layout_width">match_parent</item>
     <item name="android:layout_height">wrap_content</item>
     <item name="android:focusable">true</item>
     <item name="android:clickable">true</item>
     <item name="android:background">@drawable/bg_common_button</item>
     <item name="android:textColor">@color/white</item>
     <item name="android:textSize">@dimens/base_button_text_size</item>
 <!--  ^^ that dimen value could vary from screen size to screen size, but the style likely won't -->
 </style>

// button_layout.xml
 <Button
   android:id="@+id/styled_button"
   style="@style/BaseButton" /> <!-- and you're done -->

// some_other_layout.xml
 <LinearLayout
   style="@style/BaseLinearLayout">

   <Button style="@style/BaseButton" android:text="Button1" />
   <Button style="@style/BaseButton" android:text="Button2" />
   <Button style="@style/BaseButton" android:text="Button3" />

 </LinearLayout>

如果您想使用代码实例化该按钮,那么您可以使用 LayoutInflater 为该特定按钮的布局充气,并在任何您想要的地方使用它。事实上,您可以在 XML 中创建各种形式的组件,然后在运行时对它们进行扩展。

LayoutInflater inflater = LayoutInflater.from(YourActivity.this); 
Button theInflatedButton = inflater.inflate(R.layout.button_layout.xml, null); 

当然,典型的例子是 ListViews 和您希望填充它们的项目。您将创建一个 listview 项目布局 xml,然后在您的 Adapter 需要新的 convertView 实例时对其进行扩充。

【讨论】:

  • 非常感谢关于 XML 与编码的见解!
  • 不客气。 Gaskoin 提到的关于合并和包含的一件事是包含标签不能传递自定义定义的 xmlns 属性。这真的是我不喜欢的关于合并/包含策略的唯一事情......但是如果你从不使用包含并且总是使用 java 膨胀,那么它就不那么重要了。
猜你喜欢
  • 1970-01-01
  • 2013-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-23
  • 2016-09-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多