Android中级——ListView和RecycleView解析

news2024/11/20 11:42:32

ListView和RecycleView

  • ListView
  • RecycleView

ListView

使用步骤可看Android基础——ListView,其setAdapter()如下,回调getCount()获取Item个数

@Override
public void setAdapter(ListAdapter adapter) {
    if (mAdapter != null && mDataSetObserver != null) {
        mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }
    resetList();
    mRecycler.clear();
    if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
        mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
    } else {
        mAdapter = adapter;
    }
	......

    super.setAdapter(adapter);
    if (mAdapter != null) {
        ......
        mItemCount = mAdapter.getCount();
        checkFocus();
        mDataSetObserver = new AdapterDataSetObserver();
        mAdapter.registerDataSetObserver(mDataSetObserver);
        ......
    }......
    requestLayout();
}

创建AdapterDataSetObserver,调用ListAdapter的registerDataSetObserver(),其实现类是BaseAdapter

private final DataSetObservable mDataSetObservable = new DataSetObservable();

public void registerDataSetObserver(DataSetObserver observer) {
    mDataSetObservable.registerObserver(observer);
}

public void unregisterDataSetObserver(DataSetObserver observer) {
    mDataSetObservable.unregisterObserver(observer);
}

public void notifyDataSetChanged() {
    mDataSetObservable.notifyChanged();
}

当调用notifyDataSetChanged()时会回调AdapterDataSetObserver的notifyChanged(),其在ListView的父类AbsListView里面

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
    @Override
    public void onChanged() {
        super.onChanged();
        ......
    }
    ......
}

调用AdapterView的AdapterDataSetObserver的notifyChanged()

class AdapterDataSetObserver extends DataSetObserver {
    ......
    @Override
    public void onChanged() {
        mDataChanged = true;
        mOldItemCount = mItemCount;
        mItemCount = getAdapter().getCount();
        ......
        checkFocus();
        requestLayout();
    }
    ......
}

调用requestLayout()会重新布局,调用AdapterView的onLayout()

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
   	......
    layoutChildren();
    ......
}

调用ListView的layoutChildren(),根据mLayoutMode设置布局方式

@Override
protected void layoutChildren() {
    final boolean blockLayoutRequests = mBlockLayoutRequests;
    if (blockLayoutRequests) {
        return;
    }
    mBlockLayoutRequests = true;
    try {
        super.layoutChildren();
        invalidate();
        ......
        
        switch (mLayoutMode) {
        ......
        case LAYOUT_FORCE_BOTTOM:
            sel = fillUp(mItemCount - 1, childrenBottom);
            adjustViewsUpOrDown();
            break;
        case LAYOUT_FORCE_TOP:
            mFirstPosition = 0;
            sel = fillFromTop(childrenTop);
            adjustViewsUpOrDown();
            break;
        ......
        }......
    }
}

调用ListView的fillFromTop()会调用fillDown()从上到下布局,若不到底部且小于总数,则循环创建ItemView

private View fillDown(int pos, int nextTop) {
    View selectedView = null;
    int end = (mBottom - mTop);
    if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
        end -= mListPadding.bottom;
    }
    while (nextTop < end && pos < mItemCount) {
        boolean selected = pos == mSelectedPosition;
        View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected);
        nextTop = child.getBottom() + mDividerHeight;
        if (selected) {
            selectedView = child;
        }
        pos++;
    }
    setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - 1);
    return selectedView;
}

private View makeAndAddView(int position, int y, boolean flow, int childrenLeft, boolean selected) {
   	......
    final View child = obtainView(position, mIsScrap);
    setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
    return child;
}

调用AdapterView的obtainView(),这里会回调getItemViewType()、getView()

View obtainView(int position, boolean[] outMetadata) {
    ......
    final View transientView = mRecycler.getTransientStateView(position);	//从缓存列表mTransientStateViews指定位置获取Item
    if (transientView != null) {
        final LayoutParams params = (LayoutParams) transientView.getLayoutParams();
        if (params.viewType == mAdapter.getItemViewType(position)) {	//若ViewType没变则回调getView()获取View重新绑定数据
            final View updatedView = mAdapter.getView(position, transientView, this);
            if (updatedView != transientView) {		//若没有复用,返回的不是同一个View,则添加到ScrapView
                setItemViewLayoutParams(updatedView, position);
                mRecycler.addScrapView(updatedView, position);
            }
        }
     	......
        return transientView;
    }
    final View scrapView = mRecycler.getScrapView(position);	//若不在缓存列表,取出ScrapView列表对应位置的scrapView 
    final View child = mAdapter.getView(position, scrapView, this);	//回调getView()获取child,若scrapView为空则返回新的View,若不为空也可能返回的不是同一个View,如Header
    if (scrapView != null) {
        if (child != scrapView) {	//若scrapView和child不一样,说明是新的View,将scrapView添加到缓存列表mTransientStateViews
            mRecycler.addScrapView(scrapView, position);
        }......
    }
    ......
    setItemViewLayoutParams(child, position);
    ......
    return child;
}

以下面代码为例

 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
     ViewHolder holder = null;
     if (convertView == null) {
         holder = new ViewHolder();
         convertView = mInflater.inflate(R.layout.list_item, null);
         holder.dataIv = convertView.findViewById(R.id.list_iv);
         holder.dataTv = convertView.findViewById(R.id.list_tv);
         convertView.setTag(holder);
     } else {
         holder = (ViewHolder) convertView.getTag();
     }
     Data data = (Data) getItem(position);
     holder.dataIv.setImageResource(data.getImageId());
     holder.dataTv.setText(data.getName());
     return convertView;
 }
  • 第一次创建时convertView为空,会创建铺满屏幕个数的Item+1,如下图为8个
  • 当滑动时将Item1缓存起来,并赋值给convertView,下一个Item9就会复用Item1重新绑定数据
    在这里插入图片描述

RecycleView

使用步骤可看Android基础——RecycleView

  • onCreateViewHolder()、onBindViewHolder()相当于ListView的getView()
  • 操作对象变成ViewHolder,无需像ListView那样判断缓存

其setAdapter()方法如下

public void setAdapter(@Nullable Adapter adapter) {
    setLayoutFrozen(false);
    setAdapterInternal(adapter, false, true);
    processDataSetCompletelyChanged(false);
    requestLayout();
}

private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews) {
    if (mAdapter != null) {
        mAdapter.unregisterAdapterDataObserver(mObserver);
        mAdapter.onDetachedFromRecyclerView(this);
    }
    if (!compatibleWithPrevious || removeAndRecycleViews) {
        removeAndRecycleViews();
    }
    mAdapterHelper.reset();
    final Adapter oldAdapter = mAdapter;
    mAdapter = adapter;
    if (adapter != null) {
        adapter.registerAdapterDataObserver(mObserver);
        adapter.onAttachedToRecyclerView(this);
    }
    if (mLayout != null) {
        mLayout.onAdapterChanged(oldAdapter, mAdapter);
    }
    mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
    mState.mStructureChanged = true;
}

将RecyclerViewDataObserver注册到RecyclerViewAdapter,当调用notifyDataSetChanged()会回调onChanged()重新布局

private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();

public final void notifyDataSetChanged() {
    mObservable.notifyChanged();
}

private class RecyclerViewDataObserver extends AdapterDataObserver {
    ......
    @Override
    public void onChanged() {
        ......
        if (!mAdapterHelper.hasPendingUpdates()) {
            requestLayout();
        }
    }
   	......
}

requestLayout()会调用onLayout()、dispatchLayout()、dispatchLayoutStep1()

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    ......
    dispatchLayout();
    ......
}

void dispatchLayout() {
    ......
    if (mState.mLayoutStep == State.STEP_START) {
        dispatchLayoutStep1();
        mLayout.setExactMeasureSpecsFrom(this);
        dispatchLayoutStep2();
    }......
    dispatchLayoutStep3();
}


private void dispatchLayoutStep1() {
    ......
    if (mState.mRunPredictiveAnimations) {
        ......
        mLayout.onLayoutChildren(mRecycler, mState);
        ......
    }
	......
}

mLayout通过setLayoutManager()设置

public void setLayoutManager(@Nullable LayoutManager layout) {
    if (layout == mLayout) {
        return;
    }
    ......
    mLayout = layout;
    if (layout != null) {
        ......
        mLayout.setRecyclerView(this);
        if (mIsAttached) {
            mLayout.dispatchAttachedToWindow(this);
        }
    }
    mRecycler.updateViewCacheSize();
    requestLayout();
}

以LinearLayoutManager的onLayoutChildren()为例

public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state){
   ......
    if (mAnchorInfo.mLayoutFromEnd) {
       ......
    } else {
    	......
        fill(recycler, mLayoutState, state, false);
       ......
    }
    ......
}

int fill(RecyclerView.Recycler recycler, LayoutState layoutState, RecyclerView.State state, boolean stopOnFocusable) {
    ......
    int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;	//计算当前可用空间
    LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
    while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
        ......
        layoutChunk(recycler, state, layoutState, layoutChunkResult);	//若还有空间则循环布局Item
        ......
    }
   	......
    return start - layoutState.mAvailable;
}

void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutState layoutState, LayoutChunkResult result) {
    View view = layoutState.next(recycler);	//获取View
    ......
    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();	//获取LayoutParams
    ......
    measureChildWithMargins(view, 0, 0);	//测量View
    result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);	//计算消耗的宽高
    int left, top, right, bottom;	//计算上下左右坐标
    if (mOrientation == VERTICAL) {
        if (isLayoutRTL()) {
            right = getWidth() - getPaddingRight();
            left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
        } else {
            left = getPaddingLeft();
            right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
        }
        if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
            bottom = layoutState.mOffset;
            top = layoutState.mOffset - result.mConsumed;
        } else {
            top = layoutState.mOffset;
            bottom = layoutState.mOffset + result.mConsumed;
        }
    }......
    layoutDecoratedWithMargins(view, left, top, right, bottom);	//调用layout()
    ......
    result.mFocusable = view.hasFocusable();
}

public void layoutDecoratedWithMargins(@NonNull View child, int left, int top, int right, int bottom) {
    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
    final Rect insets = lp.mDecorInsets;
    child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
            right - insets.right - lp.rightMargin,
            bottom - insets.bottom - lp.bottomMargin);
}

View next(RecyclerView.Recycler recycler) {
    ......
    final View view = recycler.getViewForPosition(mCurrentPosition);
    mCurrentPosition += mItemDirection;
    return view;
}

next()调用RecyclerView的getViewForPosition(),回调getItemViewType()

@NonNull
public View getViewForPosition(int position) {
    return getViewForPosition(position, false);
}

View getViewForPosition(int position, boolean dryRun) {
    return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}

@Nullable
ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
    ......
    ViewHolder holder = null;
    if (mState.isPreLayout()) {
        holder = getChangedScrapViewForPosition(position);
        ......
    }
    if (holder == null) {
        holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
        ......
    }
    if (holder == null) {
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        ......
        final int type = mAdapter.getItemViewType(offsetPosition);
        if (mAdapter.hasStableIds()) {
            holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
           ......
        }
        if (holder == null && mViewCacheExtension != null) {
            ......
            final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
            if (view != null) {
                holder = getChildViewHolder(view);
                ......
            }
        }
        if (holder == null) {
            ......
            holder = getRecycledViewPool().getRecycledView(type);
         	......
        }
        if (holder == null) {	//上面是各种缓存都为空,才调用createViewHolder()
            ......
            holder = mAdapter.createViewHolder(RecyclerView.this, type);
            ......
        }
    }
    ......
    boolean bound = false;
    if (mState.isPreLayout() && holder.isBound()) {
        ......
    } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) { //这里绑定数据
        ......
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
    }
    ......
    return holder;
}

调用RecyclerView的createViewHolder()、tryBindViewHolderByDeadline()

@NonNull
public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
    try {
        final VH holder = onCreateViewHolder(parent, viewType);
        ......
        holder.mItemViewType = viewType;
        return holder;
    }......
}

private boolean tryBindViewHolderByDeadline(@NonNull ViewHolder holder, int offsetPosition, int position, long deadlineNs) {
    ......
    mAdapter.bindViewHolder(holder, offsetPosition);
    ......
    return true;
}
 
public final void bindViewHolder(@NonNull VH holder, int position) {
    ......
    onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
    ......
}

public void onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) {
    onBindViewHolder(holder, position);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1081389.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

v-model绑定input、textarea、checkbox、radio、select

1.input <div><!-- v-model绑定input --><input type"text" v-model"message"><h2>{{message}}</h2></div><script>const App{template:#my-app,data() {return {message:Hello World,}},}Vue.createApp(App).…

Java:设计模式之结构型-装饰者模式(decorator pattern)

装饰者模式(decorator pattern): 动态地将责任附加到对象上 意图&#xff1a;为对象动态添加功能 类图 实现 设计不同种类的饮料&#xff0c;饮料可以添加配料&#xff0c;比如可以添加牛奶&#xff0c;并且支持动态添加新配料。每增加一种配料&#xff0c;该饮料的价格就…

微信怎么查看名下所有微信号?

一般大家都会有两个或者更多的微信号&#xff0c;那怎么知道自己名下有几个微信号呢&#xff1f; 微信号要使用支付或者其他一些功能是要实名才可以使用的&#xff0c;有时候不知道自己的名下绑定了多少微信号&#xff0c;怎么查询呢&#xff1f; 微信最近出了开通小号的功能&…

当下测试行业中UI自动化面临的难点及如何解决

经常有人会问&#xff0c;什么样的项目才适合进行UI自动化测试呢&#xff1f;UI自动化测试相当于模拟手工测试&#xff0c;通过程序去操作页面上的控件。而在实际测试过程中&#xff0c;经常会遇到无法找到控件&#xff0c;或者因控件定义变更而带来的维护成本等问题。 哪些场…

Python数组添加元素append的时间复杂度分析

由于数组需要连续的存储空间&#xff0c;append&#xff08;&#xff09;函数的时间复杂度可能为1也可能为n&#xff0c;主要看在后面添加元素时&#xff0c;当前位置是否可以添加&#xff0c;有位置可以添加的话&#xff0c;则直接在后面添加&#xff0c;此时时间复杂度为1&am…

工学云打卡签到自动完成在异地的问题就解决了|蘑菇钉

工学云打卡助手&#xff0c;能解决你在异地时每天不间断签到的问题&#xff0c;仔细看图哦 1.自动签到 2.自定义打卡地区 3.生成日周月报与总结自动发表 4.支持随机通用内容 5.支持打卡结果推送 对于许多即将步入职场的新人来说&#xff0c;实习是一个非常重要的阶段。实习…

【算法挨揍日记】day13—— DP34 【模板】前缀和、DP35 【模板】二维前缀和

DP34 【模板】前缀和 【模板】前缀和_牛客题霸_牛客网 题目描述&#xff1a; 给定一个长度为n的数组. 接下来有q次查询, 每次查询有两个参数l, r. 对于每个询问, 请输出 输入描述: 第一行包含两个整数n和q.第二行包含n个整数, 表示.接下来q行,每行包含两个整数 l和r. …

聊聊Android签名检测7种核心检测方案详解

聊聊Android签名检测总结与反思 背景&#xff1a; 这篇文章只讲Android端签名检测&#xff0c;安卓发展到现在&#xff0c;因为国内环境没有谷歌市场&#xff0c;所以很多官方推荐的Api没法使用 &#xff0c;所以国内的签名检测方式也是“千奇百怪” 。发展至今每种方法都有一…

智安网络|揭开云服务的神秘面纱:其含义和功能的综合指南

随着信息技术的不断发展&#xff0c;云服务已经成为了我们生活中的一个不可或缺的部分。无论是在个人生活中还是在商业领域&#xff0c;云服务都具有广泛的应用。 什么是云服务&#xff1f; 云服务是一种基于互联网的计算和存储资源提供方式&#xff0c;它允许用户通过互联网访…

CTF Misc(3)流量分析基础以及原理

前言 流量分析在ctf比赛中也是常见的题目&#xff0c;参赛者通常会收到一个网络数据包的数据集&#xff0c;这些数据包记录了网络通信的内容和细节。参赛者的任务是通过分析这些数据包&#xff0c;识别出有用的信息&#xff0c;例如登录凭据、加密算法、漏洞利用等等 工具安装…

智能优化算法常用指标一键导出为EXCEL,CEC2017函数集最优值,平均值,标准差,最差值,中位数,秩和检验,箱线图...

声明&#xff1a;对于作者的原创代码&#xff0c;禁止转售倒卖&#xff0c;违者必究&#xff01; 之前出了一篇关于CEC2005函数集的智能算法指标一键统计&#xff0c;然而后台有很多小伙伴在询问其他函数集该怎么调用。今天采用CEC2017函数集为例&#xff0c;进行展示。 为了突…

手动下载/安装Xcode的simulator

目录 前言解决方案1.获取simulator包下载地址1.1 Apple后台1.2 手动 2.使用三方下载工具下载3.使用命令安装simulator 前言 Xcode某个版本更新之后不带iOS的Simulator,导致全新下载一个Xcode后没法编译项目.公司的网又很坑,每次断掉点重试都重新下载,导致完全没法下下来.特别影…

lazada商品列表数据接口,关键词搜索lazada商品数据接口

在网页抓取方面&#xff0c;可以使用 Python、Java 等编程语言编写程序&#xff0c;通过模拟 HTTP 请求&#xff0c;获取lazada网站上的商品页面。在数据提取方面&#xff0c;可以使用正则表达式、XPath 等方式从 HTML 代码中提取出有用的信息。值得注意的是&#xff0c;lazada…

大规模语言模型人类反馈对齐--强化学习

​OpenAI 推出的 ChatGPT 对话模型掀起了新的 AI 热潮&#xff0c; 它面对多种多样的问题对答如流&#xff0c; 似乎已经打破了 机器和人的边界。这一工作的背后是大型语言模型 (Large Language Model&#xff0c;LLM) 生成领域的新训练范式&#xff1a;RLHF (Reinforcement Le…

专题二:二叉树的深搜【递归、搜索、回溯】

深度优先遍历&#xff08;DFS&#xff0c;全称为DepthFirstTraversal&#xff09;&#xff0c;是我们树或者图这样的数据结构中常用的⼀种遍历算法。这个算法会尽可能深的搜索树或者图的分⽀&#xff0c;直到⼀条路径上的所有节点都被遍历完毕&#xff0c;然后再回溯到上⼀层&a…

为什么要做CRM?

客户管理的痛点&#xff1a; 1、销售经常性漏跟错跟客户&#xff0c;客户转化率低造成资源浪费 2、客户信息繁杂&#xff0c;难整理和查找 3、销售离职带走客户资源&#xff0c;损失大 4、传统报价审批流程长&#xff0c;效率低 企业做CRM系统有以下几点好处&#xff1a; …

纸、纸板和纸制品 有效回收组分的测定

声明 本文是学习GB-T 42944-2023 纸、纸板和纸制品 有效回收组分的测定. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件描述了纸、纸板和纸制品中有效回收组分的测定方法。 本文件适用于各种纸、纸板和纸制品&#xff0c;也适用于铝箔…

Paddle GPU版本需要安装CUDA、CUDNN

完整的教程 深度学习环境配置&#xff1a;linuxwindows系统下的显卡驱动、Anaconda、Pytorch&Paddle、cuda&cudnn的安装与说明 - 知乎这篇文档的内容是尽量将深度学习环境配置(使用GPU)所需要的内容做一些说明&#xff0c;由于笔者只在windows和linux下操作过&#xf…

Zookeeper分布式一致性协议ZAB源码剖析

文章目录 1、ZAB协议介绍2、消息广播 1、ZAB协议介绍 ZAB 协议全称&#xff1a;Zookeeper Atomic Broadcast&#xff08;Zookeeper 原子广播协议&#xff09;。 Zookeeper 是一个为分布式应用提供高效且可靠的分布式协调服务。在解决分布式一致性方面&#xff0c;Zookeeper 并…

04_学习springdoc与oauth结合_简述

文章目录 1 前言2 基本结构3 需要做的配置 简述4 需要做的配置 详述4.1 backend-api-gateway 的配置4.1.1 application.yml 4.2 backend-film 的配置4.2.1 pom.xml 引入依赖4.2.2 application.yml 的配置4.2.3 Spring Security 资源服务器的配置类 MyResourceServerConfig4.2.4…