13

对于定义为主题的按钮,我有自己的风格,但我也使用自己的类来处理按钮(因为有自己的字体)。是否可以使用漂亮的名称来调用我的按钮,例如

<MyButton>

代替

<com.wehavelongdomainname.android.ui.MyButton>
4

4 回答 4

16

因此,令人惊讶的是,答案是“是”。我最近了解到这一点,实际上你可以做一些事情来让你的自定义视图膨胀更有效。IntelliJ 仍然警告您它无效(尽管它会成功编译和运行)——我不确定 Eclipse 是否警告您。

无论如何,所以你需要做的是定义你自己的子类LayoutInflater.Factory

public class CustomViewFactory implements LayoutInflater.Factory {
    private static CustomViewFactory mInstance;

    public static CustomViewFactory getInstance () {
        if (mInstance == null) {
            mInstance = new CustomViewFactory();
        }

        return mInstance;
    }

    private CustomViewFactory () {}

    @Override
    public View onCreateView (String name, Context context, AttributeSet attrs) {
        //Check if it's one of our custom classes, if so, return one using
        //the Context/AttributeSet constructor
        if (MyCustomView.class.getSimpleName().equals(name)) {
            return new MyCustomView(context, attrs);
        }

        //Not one of ours; let the system handle it
        return null;
    }
}

然后,在您为包含这些自定义视图的布局充气的任何活动或上下文中,您需要将您的工厂分配给LayoutInflater该上下文:

public class CustomViewActivity extends Activity {
    public void onCreate (Bundle savedInstanceState) {
        //Get the LayoutInflater for this Activity context
        //and set the Factory to be our custom view factory
        LayoutInflater.from(this).setFactory(CustomViewFactory.getInstance());

        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_with_custom_view);
    }
}

然后,您可以在 XML 中使用简单的类名:

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

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:orientation="vertical"
             android:layout_width="match_parent"
             android:layout_height="match_parent">

    <MyCustomView
        android:id="@+id/my_view"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_gravity="center_vertical" />

</FrameLayout>
于 2013-07-04T21:26:25.613 回答
2

定义您自己的 LayoutInflater.Factory 子类对我来说似乎需要做很多工作。只需使用一些通用代码覆盖 Activity 的 onCreateView() :

@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {

    View view;

    // No need wasting microseconds getting the inflater every time.
    // This method gets called a great many times.
    // Better still define these instance variables in onCreate()
    if (mInflator == null){

        mInflator = LayoutInflater.from(context);

        mPrefix = ((Activity) context).getComponentName().getClassName();

        // Take off the package name including the last period
        // and look for custom views in the same directory.
        mPrefix = mPrefix.substring(0, mPrefix.lastIndexOf(".")+1);
    }

    // Don't bother if 'a path' is already specified.
    if (name.indexOf('.') > -1) return null;

    try{

        view = mInflator.createView(name, mPrefix, attrs);

    } catch (ClassNotFoundException e) {

        view = null;

    } catch (InflateException e) {

        view = null;
    }

    // Returning null is no big deal. The super class will continue the inflation.
    return view;
}

请注意,自定义视图必须与此活动位于同一包中(即在同一目录中),但它只是您可以在任何活动中添加的通用代码(或者更好的是,从自定义父活动类继承)。您不必担心寻找 kcoppock 提供的解决方案中指定的特定类:

if (MyCustomView.class.getSimpleName().equals(name)) {....

你当然不是在创建一个全新的类。

真正的魔力在于核心库类 LayoutInflator.java。请参阅下面的调用 mPrivateFactory.onCreateView()?:

    if (view == null && mPrivateFactory != null) {
        view = mPrivateFactory.onCreateView(parent, name, mContext, attrs);
    }

    if (view == null) {
        if (-1 == name.indexOf('.')) {
            view = onCreateView(parent, name, attrs);
        } else {
            view = createView(name, null, attrs);
        }
    }

您会看到,如果所谓的 mPrivateFactory 返回 null(顺便说一下,mPrivateFactory 恰好是您的活动类),则 LayoutInflator 只是继续使用它的其他替代方法并继续膨胀:

if (view == null) {
   if (-1 == name.indexOf('.')) {
      view = onCreateView(parent, name, attrs);
   } else {
      view = createView(name, null, attrs);
   }
}

使用 IDE 调试器“遍历”库类并真正了解 Android 的工作原理是个好主意。:)

请注意,代码 ,if (-1 == name.indexOf('.')) {适用于仍然坚持使用自定义视图放入完整路径的人,<com.wehavelongdomainname.android.ui.MyButton>如果名称中有“点”,则使用前缀(第二个参数)调用 creatview()无效的:view = createView(name, null, attrs);

为什么我使用这种方法是因为我发现在初始开发过程中有时会移动(即更改)包名称。但是,与在 java 代码本身中执行的包名称更改不同,编译器不会捕获现在出现在任何 XML 文件中的此类更改和差异。使用这种方法,现在不必了。

干杯。

于 2015-05-06T02:21:37.747 回答
1

你也可以这样做:

<view
  class="com.wehavelongdomainname.android.ui.MyButton" 
  ... />

参看。http://developer.android.com/guide/topics/ui/custom-components.html#modifying

于 2014-12-18T19:03:03.143 回答
-1

不,您需要为您的课程提供“完整路径”,否则框架将无法扩展您的布局。

于 2013-07-04T21:11:00.033 回答