Android DataBinding单向数据绑定深入探究

目录
  • 一、数据绑定流程
  • 二、建立观察者模式绑定关系

在前面DataBinding原理----布局的加载这篇文章中,我们说明了DataBinding中布局的加载过程,这里继续下一步,数据是如何进行绑定的,这里只介绍单向数据绑定,即数据的变化会反映到控件上;后面再介绍双向数据绑定。

在分析源码之前,在心里要有一个概念就是这里的数据绑定是基于观察者模式来实现的,所以在阅读这部分源码的时候要着重分清楚,谁是观察者谁是被观察者,把这个思想放在心理,这样就能抓住代码的本质。

这一篇分为两个小部分,首先是数据的绑定流程分析,然后是观察者模式绑定关系的建立流程。

一、数据绑定流程

代码分析,走起。还是贴一段Activity的代码如下:

class MainActivity : AppCompatActivity() {
    private val viewModel: SimpleViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.lifecycleOwner = this
        binding.viewModel = viewModel
    }
}

数据绑定的关键就是binding.viewModel = viewModel这行代码,其调用的方法实现如下:

    public void setViewModel(@Nullable com.zfang.databindingstudy.module.SimpleViewModel ViewModel) {
        this.mViewModel = ViewModel;
        synchronized(this) {
            mDirtyFlags |= 0x4L;
        }
        notifyPropertyChanged(BR.viewModel);
        super.requestRebind();
    }

notifyPropertyChanged这一行其实就是在通知观察者,数据发生变化了。不过这不是我们这里关注的重点(不过注意这里的mDirtyFlags 已经被修改了,已经有值)。重点在后面的那一行super.requestRebind(),其实现如下:

  protected void requestRebind() {
        //处理有include标签的情况
        if (mContainingBinding != null) {
            mContainingBinding.requestRebind();
        } else {
            //我们的场景下走的是这里
            final LifecycleOwner owner = this.mLifecycleOwner;
            if (owner != null) {
                //如果生命周期不是至少STARTED则返回
                Lifecycle.State state = owner.getLifecycle().getCurrentState();
                if (!state.isAtLeast(Lifecycle.State.STARTED)) {
                    return; // wait until lifecycle owner is started
                }
            }
            synchronized (this) {
                if (mPendingRebind) {
                    return;
                }
                //置标志位
                mPendingRebind = true;
            }
            if (USE_CHOREOGRAPHER) {
                //走这里,使用垂直同步刷新
                mChoreographer.postFrameCallback(mFrameCallback);
            } else {
                mUIThreadHandler.post(mRebindRunnable);
            }
        }
    }

首先判断是不是属于include标签的情况(也就是布局中包含include标签),如果不是则走到else分支,然后判断生命周期是不是至少STARTED状态,如果不是则不执行数据绑定(生命周期恢复的时候会再执行数据绑定);如果满足生命周期要求,则继续判断,首先把mPendingRebind 置位(避免重复绑定数据),然后使用垂直同步刷新机制post了一个callback,callback实现(位于ViewDataBinding的构建函数中)如下:

    if (USE_CHOREOGRAPHER) {
        mChoreographer = Choreographer.getInstance();
        mFrameCallback = new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
                mRebindRunnable.run();
            }
        };
    } else {
        mFrameCallback = null;
        mUIThreadHandler = new Handler(Looper.myLooper());
    }

其实最终走的还是mRebindRunnable,其实现如下:

    private final Runnable mRebindRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (this) {
                mPendingRebind = false;
            }
            //处理弱引用队列中的监听器,避免内在泄漏
            processReferenceQueue();
            //如果是Android4.4及以后走这里
            if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
                // Nested so that we don't get a lint warning in IntelliJ
                //如果View还没attach上,则返回;后面attach上会再执行数据绑定
                if (!mRoot.isAttachedToWindow()) {
                    // Don't execute the pending bindings until the View
                    // is attached again.
                    mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
                    mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
                    return;
                }
            }
            //执行数据绑定.
            executePendingBindings();
        }
    };

嗯,代码中都写了注释。首先处理弱引用队列,避免内存泄漏,然后判断如果View还没attach则返回,后面attach上再执行数据绑定。最后调用executePendingBindings,执行数据绑定。

    public void executePendingBindings() {
        if (mContainingBinding == null) {
            //没有include会走这里
            executeBindingsInternal();
        } else {
            mContainingBinding.executePendingBindings();
        }
    }

我们的场景下走executeBindingsInternal,代码如下:

    private void executeBindingsInternal() {
        if (mIsExecutingPendingBindings) {
            requestRebind();
            return;
        }
        //这里调用了apt代码生存的方法,也就是我们工程中的方法。
        if (!hasPendingBindings()) {
            return;
        }
        mIsExecutingPendingBindings = true;
        mRebindHalted = false;
        if (mRebindCallbacks != null) {
            mRebindCallbacks.notifyCallbacks(this, REBIND, null);
            // The onRebindListeners will change mPendingHalted
            if (mRebindHalted) {
                mRebindCallbacks.notifyCallbacks(this, HALTED, null);
            }
        }
        if (!mRebindHalted) {
            //这里执行绑定
            executeBindings();
            if (mRebindCallbacks != null) {
                mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
            }
        }
        mIsExecutingPendingBindings = false;
    }

上面主要是一些条件判断,避免重复执行绑定。其中方法hasPendingBindings判断有没有需要执行数据绑定,最后executeBindings调用执行数据绑定;hasPendingBindings的实现位于ActivityMainBindingImpl.java中,也就是DataBinding帮我们生存的类,代码如下:

   //ActivityMainBindingImpl.java
   public boolean hasPendingBindings() {
        synchronized(this) {
            if (mDirtyFlags != 0) {
                return true;
            }
        }
        return false;
    }

上面我们说过mDirtyFlags 在setViewModel方法调用的时候参数已经不为0了,所以这里会返回true,也就是后面会执行到executeBindings方法,其实现也位于ActivityMainBindingImpl.java类中,代码如下:

    @Override
    protected void executeBindings() {
        long dirtyFlags = 0;
        synchronized (this) {
            //复制位标识,记录了哪些数据发生变化。
            dirtyFlags = mDirtyFlags;
            mDirtyFlags = 0;
        }
        java.lang.String viewModelFirstGetValue = null;
        androidx.lifecycle.LiveData<java.lang.String> viewModelSecond = null;
        androidx.lifecycle.LiveData<java.lang.String> viewModelFirst = null;
        java.lang.String viewModelSecondGetValue = null;
        com.zfang.databindingstudy.module.SimpleViewModel viewModel = mViewModel;
        //根据dirtyFlags 判断哪些数据发生变化,一个变量会对应到二进制里面的一个位
        //该位不为0,说明数据有变化,于是会执行相应的获得数据逻辑。
        if ((dirtyFlags & 0xfL) != 0) {
            if ((dirtyFlags & 0xdL) != 0) {
                if (viewModel != null) {
                    // read viewModel.second
                    viewModelSecond = viewModel.getSecond();
                }
                updateLiveDataRegistration(0, viewModelSecond);
                if (viewModelSecond != null) {
                    // read viewModel.second.getValue()
                    viewModelSecondGetValue = viewModelSecond.getValue();
                }
            }
            if ((dirtyFlags & 0xeL) != 0) {
                if (viewModel != null) {
                    // read viewModel.first
                    viewModelFirst = viewModel.getFirst();
                }
                //建立观察者绑定关系
                updateLiveDataRegistration(1, viewModelFirst);
                if (viewModelFirst != null) {
                    // read viewModel.first.getValue()
                    viewModelFirstGetValue = viewModelFirst.getValue();
                }
            }
        }
        // batch finished
        //根据上面拿到的数据调用setText,也就是设置了相应的数据到UI上,实现
        //了数据的变化更新的UI逻辑。
        if ((dirtyFlags & 0xeL) != 0) {
            // api target 1
            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.first, viewModelFirstGetValue);
        }
        if ((dirtyFlags & 0xdL) != 0) {
            // api target 1
            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.second, viewModelSecondGetValue);
        }
    }
    // Listener Stub Implementations
    // callback impls
    // dirty flag
    private long mDirtyFlags = 0xffffffffffffffffL;
    /* flag mapping
        flag 0 (0x1L): viewModel.second
        flag 1 (0x2L): viewModel.first
        flag 2 (0x3L): viewModel
        flag 3 (0x4L): null
    flag mapping end*/
    //end

首先说明下,我们的model里面一般来说会有几个变量,其实每一个变量在DataBinding中都会有一个二进制位来标识当前数据是否发生了变化,如果发生变化,则该位置1,然后用dirtyFlags 作一个“与”运算就可以判断出该数据位是否发生了变化(好好体会下)。

对应到我们的场景中,flag 0 (0x1L): viewModel.second这个标识就对应到second这个变量,而flag 1 (0x2L): viewModel.first就对应到first这个变量,再用dirtyFlags作运算就知道哪个位发生变化。

等等,数据是已经绑定了,怎么没看到观察者模式的绑定关系?其实上面的代码注释已经说明了,这行代码updateLiveDataRegistration正是建立了观察者模式的绑定关系。

终于看到数据的绑定了,下面继续看下updateLiveDataRegistration是如何建立观察者模式的绑定关系。

二、建立观察者模式绑定关系

首先贴上updateLiveDataRegistration的代码如下:

    //ViewDataBinding.java
    protected boolean updateLiveDataRegistration(int localFieldId, LiveData<?> observable) {
        mInLiveDataRegisterObserver = true;
        try {
            return updateRegistration(localFieldId, observable, CREATE_LIVE_DATA_LISTENER);
        } finally {
            mInLiveDataRegisterObserver = false;
        }
    }

首先作下参数说明,第一个参数说明是哪个位置上的变量,第二个是个LiveData(对应到了我们在ViewModel中定义的数据变量,也就是要被观察的变量)。这里只是简单的转发调用下updateRegistration,最后一个参数是一个creator(利用了工厂方法模式),其实现如下:

    private static final CreateWeakListener CREATE_LIVE_DATA_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(
                ViewDataBinding viewDataBinding,
                int localFieldId,
                ReferenceQueue<ViewDataBinding> referenceQueue
        ) {
            return new LiveDataListener(viewDataBinding, localFieldId, referenceQueue)
                    .getListener();
        }
    };

就是创建了一个LiveDataListener对象,并调用了它的getListener方法,该方法会返回一个WeakListener类型的变量。继续上面的updateRegistration方法,代码如下:

    protected boolean updateRegistration(int localFieldId, Object observable,
            CreateWeakListener listenerCreator) {
        //由于observable是一个livedata对象且不空,所以不会走这里。
        if (observable == null) {
            return unregisterFrom(localFieldId);
        }
        WeakListener listener = mLocalFieldObservers[localFieldId];
        //因为是第一次进入这里,所以会进入到registerTo中
        if (listener == null) {
            registerTo(localFieldId, observable, listenerCreator);
            return true;
        }
        if (listener.getTarget() == observable) {
            return false;//nothing to do, same object
        }
        unregisterFrom(localFieldId);
        registerTo(localFieldId, observable, listenerCreator);
        return true;
    }

根据代码中的注释可知,如果是第一次进入最后会进入到registerTo方法中,其实现如下:

    protected void registerTo(int localFieldId, Object observable,
            CreateWeakListener listenerCreator) {
        if (observable == null) {
            return;
        }
        WeakListener listener = mLocalFieldObservers[localFieldId];
        if (listener == null) {
            //上面说过这里返回的是个WeakListener对象。
            listener = listenerCreator.create(this, localFieldId, sReferenceQueue);
            //把对象保存起来
            mLocalFieldObservers[localFieldId] = listener;
            //设置lifeCycle
            if (mLifecycleOwner != null) {
                listener.setLifecycleOwner(mLifecycleOwner);
            }
        }
        //建立观察者绑定关系
        listener.setTarget(observable);
    }

首先判断如果listener则创建,返回的是一个WeakListener对象(其实中间还有一个LiveDataListener对象,WeakListener对象的mObservable变量会持有LiveDataListener对象),然后保存到mLocalFieldObservers数组中,看上去就是一个观察者数组对象,最后调用WeakListener的setTarget方法,其实现如下:

    public void setTarget(T object) {
        //这里的object就是传递过来的liveData对象,下面会把它保存到target中
        unregister();
        mTarget = object;
        if (mTarget != null) {
            //mObservable其实就是LiveDataListener对象
            mObservable.addListener(mTarget);
        }
    }

重复下,上面的mTarget就是ViewModel中的LiveData对象,mObservable其实就是LiveDataListener对象,所以调用了LiveDataListener的addListener方法,实现如下:

    //ViewDataBinding$LiveDataListener
    public void addListener(LiveData<?> target) {
        //target是LiveData
        LifecycleOwner lifecycleOwner = getLifecycleOwner();
        if (lifecycleOwner != null) {
            //LiveData监听了lifecycleOwner的生命周期变化。
            target.observe(lifecycleOwner, this);
        }
    }

很简单的逻辑,就是通过target.observe(lifecycleOwner, this)建立了观察者模式的绑定关系。之后生命周期发生变化就会调用到onChange方法,代码如下:

        public void onChanged(@Nullable Object o) {
            ViewDataBinding binder = mListener.getBinder();
            if (binder != null) {
                binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0);
            }
        }

这里的binder其实就是ActivityMainBindingImpl,随后调用handleFieldChange,代码如下:

    protected void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
        if (mInLiveDataRegisterObserver || mInStateFlowRegisterObserver) {
            // We're in LiveData or StateFlow registration, which always results in a field change
            // that we can ignore. The value will be read immediately after anyway, so
            // there is no need to be dirty.
            return;
        }
        //调用了子类实现,也就是ActivityMainBindingImpl
        boolean result = onFieldChange(mLocalFieldId, object, fieldId);
        if (result) {
            //重新绑定数据
            requestRebind();
        }
    }

首先调用了onFiledChange,它的实现位于apt生存的代码中,如下:

    @Override
    protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
        switch (localFieldId) {
            case 0 :
                return onChangeViewModelSecond((androidx.lifecycle.LiveData<java.lang.String>) object, fieldId);
            case 1 :
                return onChangeViewModelFirst((androidx.lifecycle.LiveData<java.lang.String>) object, fieldId);
        }
        return false;
    }
    private boolean onChangeViewModelSecond(androidx.lifecycle.LiveData<java.lang.String> ViewModelSecond, int fieldId) {
        if (fieldId == BR._all) {
            synchronized(this) {
                    mDirtyFlags |= 0x1L;
            }
            return true;
        }
        return false;
    }
    private boolean onChangeViewModelFirst(androidx.lifecycle.LiveData<java.lang.String> ViewModelFirst, int fieldId) {
        if (fieldId == BR._all) {
            synchronized(this) {
                    mDirtyFlags |= 0x2L;
            }
            return true;
        }
        return false;
    }

根据哪些数据发生变化,返回true或者false,如果返回true,之后会调用requestRebind,不正是对应开了开头中分析的函数了吗?形成闭环。从而完成了整个观察者模式的建立与响应流程。

这一篇中分析了数据的绑定与观察者模式的建立流程,如果想了解DataBinding中布局的加载可以看前一篇DataBinding原理----布局的加载。下一篇将会分析双向数据绑定流程。

到此这篇关于Android DataBinding单向数据绑定深入探究的文章就介绍到这了,更多相关Android DataBinding 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android开发使用Databinding实现关注功能mvvp

    目录 正文 目标 Modle Presenter 正文 说到关注功能,可能很多小伙伴要说了.谁不会写 但是没有合理的架构,大家写出来的代码很可能是一大堆的复制粘贴.比如十几个页面,都有这个关注按钮.然后,你是不是也要写十几个地方呢 然后修改的时候是不是也要修改十几个地方 我们是否考虑过一下几个问题? 可复用性 (是否重复代码和逻辑过多?) 可扩展性 (比如我这里是关注的人,传userId,下个地方又是文章 articleId) 可读性 冗余代码过多,势必要影响到可读性. 然后再看下自己写的代码,

  • Android Jetpack组件支持库DataBinding与ViewModel与LiveData及Room详解

    目录 一.官方推荐的Jetpack架构 二.添加依赖 三.创建Repository 四.创建ViewModel 五.activity中使用 Android Jetpack之ViewModel.LiveData Android Jetpack之LifeCycle 一.官方推荐的Jetpack架构 ViewModel是介于View(视图)和Model(数据模型)之间的中间层,能够使视图和数据分离,又能提供视图和数据之间的通信. LiveData是一个能够在ViewModel中数据发生变化时通知页面刷

  • Android DataBinding类关系深入探究

    目录 一.在相应的板块中开启DataBinding 二.DataBing的简单使用 三.生成的xml布局 四.生存的代码 一.在相应的板块中开启DataBinding dataBinding {        enabled true    } 二.DataBing的简单使用 这里写一个简单的布局,如下: <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget

  • Android基础入门之dataBinding的简单使用教程

    目录 前言 1.前期准备 1.1打开dataBinding 1.2修改布局文件 1.3修改Activity方法 2.DataBinding的使用 2.1属性更新 2.2<data>标签 2.2.1简单数据的定义与绑定 2.2.2复杂数据的定义与绑定 2.3事件绑定 2.3.1点击事件绑定 2.3.2点击事件回传数据 2.3.3动态改变对象数据在控件上显示 2.3.4动态改变基本数据在控件上显示 2.4与输入控件结合 2.5与图片控件结合 总结 前言 dataBinding是实现 view 和

  • Android浅析viewBinding和DataBinding

    目录 viewBinding 优点 配置 使用 源码解析 DataBinding 配置 创建实体类 创建布局 创建viewModel dataBinding绑定 viewBinding 优点 当一个页面布局出现多个控件时,使用findViewById去进行控件绑定,过于冗长,且存在NULL指针异常风险.viewBinding直接创建对视图的引用,不存在因控件ID不存在而引发的NULL指针异常.并且在绑定类中对控件添加@NonNull注解 findViewById viewBinding 冗长 简

  • Android Jetpack组件DataBinding详解

    目录 Android之DataBinding DataBinding DataBinding的优势 亮点 使用DataBinding 单向绑定数据 双向绑定 Android之DataBinding DataBinding 数据绑定 DataBinding的优势 代码更加简洁,可读性会更高.部分和UI控件有关的代码可以在布局文件当中完成. 不需要使用findViewById()方法. 布局文件可以完成简单的业务逻辑处理. 亮点 开发中不需要持有控件的引用 拥有双向绑定的特性 数据与UI同步 使用D

  • Android JetPack组件的支持库Databinding详解

    目录 简介 启用databinding 布局xml variable (变量标签) data (数据标签) @{}表达式 绑定普通数据 绑定可观察数据 对单个变量的绑定-fields 对集合的绑定-collections 绑定对象-objects 绑定LiveData 双向绑定 简介 DataBinding 是 Google 在 Jetpack 中推出的一款数据绑定的支持库,利用该库可以实现在页面组件中直接绑定应用程序的数据源.使其维护起来更加方便,架构更明确简介. DataBinding 唯一

  • Android DataBinding单向数据绑定深入探究

    目录 一.数据绑定流程 二.建立观察者模式绑定关系 在前面DataBinding原理----布局的加载这篇文章中,我们说明了DataBinding中布局的加载过程,这里继续下一步,数据是如何进行绑定的,这里只介绍单向数据绑定,即数据的变化会反映到控件上:后面再介绍双向数据绑定. 在分析源码之前,在心里要有一个概念就是这里的数据绑定是基于观察者模式来实现的,所以在阅读这部分源码的时候要着重分清楚,谁是观察者谁是被观察者,把这个思想放在心理,这样就能抓住代码的本质. 这一篇分为两个小部分,首先是数据

  • Android Data Binding数据绑定详解

    去年谷歌 I/O大会上介绍了一个非常厉害的新框架DataBinding, 数据绑定框架给我们带来了很大的方便,以前我们可能需要在每个Activity里写很多的findViewById,不仅麻烦,还增加了代码的耦合性,如果我们使用DataBinding,就可以抛弃那么多的findViewById,省时省力.说到这里,其实网上也有很多快速的注解框架,但是注解框架与DataBinding想比还是不好用,而且官网文档说DataBinding还能提高解析XML的速度,其实DataBinding的好用,不仅

  • Android dataBinding与ListView及事件详解

    今天来了解一下Android最新给我们带来的数据绑定框架--Data Binding Library.数据绑定框架给我们带来了更大的方便性,以前我们可能需要在Activity里写很多的findViewById,烦人的代码也增加了我们代码的耦合性,现在我们马上就可以抛弃那么多的findViewById.说到这里,有人可能会有个疑问:我使用一些注解框架也可以不用findViewById啊,是的,但是注解注定要拖慢我们代码的速度,Data Binding则不会,官网文档说还会提高解析XML的速度,最主

  • 浅析Android企业级开发数据绑定技术

    这篇文章通过发文的方式让大家知道什么是数据绑定,以及为什么要用数据绑定等问题,有助于大家理解Android企业级开发数据绑定技术. 首先要了解什么是数据绑定?为什么要用数据绑定?怎么用数据绑定? 语法的使用 简单例子,数据绑定textview控件,一般情况下我们都是些一个布局文件,然后布局文件里放一些textview,然后通过Activity来findviewbyid来获取id,然后对象.setText("").现在我们学了数据绑定,就可以不用之前的方法了. 把库加载进来:因为这个东西

  • Android DataBinding的官方双向绑定示例

    在Android Studio 2.1 Preview 3之后,官方开始支持双向绑定了. 可惜目前Google并没有在Data Binding指南里面加入这个教程,并且在整个互联网之中只有这篇文章介绍了如何使用反向绑定. 在阅读一下文章之前,我假设你已经知道如何正向绑定. 回顾一下Data Binding 在正向绑定中,我们在Layout里面的绑定表达式是这样的: <layout ...> <data> <variable type="com.example.mya

  • Android DataBinding手把手入门教程

    1.在build.gradle(Module)里引入依赖,然后重构(sync Now): android { ...... dataBinding{ enabled true } } 2.找到想要改为dataBinding视图的页面,alt+enter弹出如下: 并选择Convert to data binding layout自动转换. 转换之后效果应和下图类似: 可以看到页面出现了新的Layout和data的空标签.(data 就是用来存放数据的) 3.接下来到对应的Activity里,声明

  • Android Handler源码深入探究

    1.android 消息循环有4个重要的类Handler.Message.Looper.MessageQueue handler 用来发送.处理消息. Message 是消息的载体. MessageQueue 是一个消息队列,既然是队列,就有入队.出队的处理. Looper 创建一个消息循环.不断的从MessageQueue中读取消息.并分发给相应的Handler进行处理. 2.我们都知道main函数是Java程序的入口,android程序也不例外. android App的唯一入口就是Acti

  • Android Activity状态与操作探究

    目录 知晓当前是在哪一个Activiy 随时随地退出程序 启动Activity的最佳写法 知晓当前是在哪一个Activiy 创建一个BaseActivity类,继承AppCompatActivity类.重写onCreate方法 open class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState)

  • Android Coil对比Glide深入分析探究

    目录 Coil概述 Glide概述 Glide VS Coil 对于小图片 对于大图片 总结 Coil概述 Coil是Android上的一个全新的图片加载框架,它的全名叫做coroutine image loader,即协程图片加载库. 与传统的图片加载库Glide,Picasso或Fresco等相比.该具有轻量(只有大约1500个方法).快.易于使用.更现代的API等优势. 它支持GIF和SVG,并且可以执行四个默认转换:模糊,圆形裁剪,灰度和圆角.并且是全用Kotlin编写,如果你是纯Kot

  • Android中 自定义数据绑定适配器BaseAdapter的方法

    复制代码 代码如下: public class PersonAdapter extends BaseAdapter { private List persons;// 要绑定的数据 private int resource;// 绑定的一个条目界面的id,此例中即为item.xml private LayoutInflater inflater;// 布局填充器,它可以使用一个xml文件生成一个View对象,可以通过Context获取实例对象 public PersonAdapter(Conte

随机推荐