Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/227.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 在XML中使用自定义视图而不使用完全限定的类名_Android_Android Layout - Fatal编程技术网

Android 在XML中使用自定义视图而不使用完全限定的类名

Android 在XML中使用自定义视图而不使用完全限定的类名,android,android-layout,Android,Android Layout,对于定义为主题的按钮,我有自己的风格,但我也使用自己的类来处理按钮(因为有自己的字体)。可以用一个漂亮的名字来称呼我的按钮吗 <MyButton> 而不是 <com.wehavelongdomainname.android.ui.MyButton> 否。您需要为类提供“完整路径”,否则框架将无法扩展您的布局。因此,令人惊讶的是,答案是“是”。我最近了解到了这一点,实际上,这是您可以做的事情,以使自定义视图更有效。IntelliJ仍然警告您它无效(尽管它将成功编译和

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

<MyButton>

而不是

<com.wehavelongdomainname.android.ui.MyButton>

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

因此,令人惊讶的是,答案是“是”。我最近了解到了这一点,实际上,这是您可以做的事情,以使自定义视图更有效。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>

您也可以这样做:

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


cf.

定义自己的LayoutInflater.Factory子类对我来说似乎有很多工作要做。 只需使用一些通用代码覆盖活动的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;
}
请注意,自定义视图必须与此活动位于同一个包(即同一目录)中,但它只是一段通用代码,您可以在任何活动中使用它(或者更好,从自定义父活动类继承)。 您不必担心寻找KCopock提供的解决方案中指定的特定类:

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

您当然不会创建一个全新的类

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

您可以看到,如果所谓的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('.){
,是为那些仍然坚持使用自定义视图输入完整路径的人编写的,
如果名称中有一个“点”,则调用creatview(),前缀(第二个参数)为null:
view=createView(name,null,attrs);

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


干杯。

我在想,也许可以用xml定义一个将此类作为父类的资源。不是吗?@KCopock answer展示了如何帮助框架进行搜索,但我远没有看到任何真正的好处。这相当于维护的复杂性。@MarcinOrlowski在内部,如果您没有提供LayoutInflater,所以我想说这应该会提高性能(尽管我怀疑这是一个明显的改进),但我同意它确实增加了一些开销。Eclipse告诉我没有LayoutInflater。Factory@Ragnar它从API 1开始就存在了()。你为它添加了正确的导入吗?找到了,我只需要导入android.view.LayoutInflaterInteresting答案,但为什么它比使用完全限定名称更有效?编辑:nevermind,你在Marcin的答案下面回答。如果没有此反射,将使用它,尽管很小-improvement@wasyl只是避免使用反射to查找构造函数(因为您直接以这种方式调用它)。由于我相信它会缓存构造函数一段时间,所以在方案上没有太大的区别。我不会仅仅为了效率而实现它。:)真的吗?这对棒棒糖也有效吗?这是一个新的:)@milosmns我不认为它一般都是新的。(也许你的意思是新的。)是的,以前没用过。很好!
if (view == null) {
   if (-1 == name.indexOf('.')) {
      view = onCreateView(parent, name, attrs);
   } else {
      view = createView(name, null, attrs);
   }
}