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

qhhrdooz  于 2023-04-04  发布在  Android
关注(0)|答案(5)|浏览(79)

我有我自己的风格的按钮定义为主题,但我也使用我自己的类来处理按钮(因为自己的字体)。

<MyButton>

而不是

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

q35jwt9p1#

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

zbdgwd5y2#

定义你自己的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;
}

请注意,自定义视图必须与此Activity位于同一个包中(即在同一个目录中),但它只是一段通用代码,您可以在任何Activity中插入(甚至更好,从自定义父Activity类继承)。您不必担心查找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恰好是你的activity类),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>如果名称中有一个'dot',那么creatview()被调用,前缀(第二个参数)为null:view = createView(name, null, attrs);
我之所以使用这种方法是因为我发现在最初的开发过程中,有时候包名会被移动(即更改)。然而,与java代码本身执行的包名更改不同,编译器不会捕获任何XML文件中出现的此类更改和差异。使用这种方法,现在它不必这样做了。
干杯。

cig3rfwq

cig3rfwq3#

您还可以执行以下操作:

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

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

fd3cxomn

fd3cxomn4#

这里的答案都不适用于Android Studio布局设计器预览版。我试图将我的自定义视图类放在android.widget包中,然后编译并运行put still,设计器预览版对此不满意。
最后,我只是把我的自定义视图类放在view包中,这样在XML中它看起来像:

<view.FancyWidget />

对我来说够短了。

n7taea2i

n7taea2i5#

不可以。你需要给你的类一个“完整路径”,否则框架将不能膨胀你的布局。

相关问题