ListView注意的问题和源码解析(上)
来源:互联网 发布:软件测试技术教程 编辑:程序博客网 时间:2024/05/23 15:39
设置没有数据时显示的默认布局
setEmptyView这个方法传入的是一个view,
TextView emptytext=new TextView(this);emptytext.setText("the list is empty");listview.setEmptyView(emptytext);上面的代码有什么问题呢?当listview为空时,emptytext能正常显示吗?
运行测试之后会发现,emptytext没有显示,是因为没有设置大小吗?debug代码,
查看当前的root布局,子布局中怎么也找不到emptytext这个view。这个布局根本就没有加载,更别说绘制了。
通过源码可以发现setEmptyView方法只是把AdapterView的属性mEmptyView设置为emptyView
@android.view.RemotableViewMethod public void setEmptyView(View emptyView) { mEmptyView = emptyView; // If not explicitly specified this view is important for accessibility. if (emptyView != null && emptyView.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) { emptyView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); } final T adapter = getAdapter(); final boolean empty = ((adapter == null) || adapter.isEmpty()); updateEmptyStatus(empty); }
在更新数据时,在更新数据时控制mEmptyView和listVeiw的显示状态
private void updateEmptyStatus(boolean empty) { if (isInFilterMode()) { empty = false; } if (empty) { if (mEmptyView != null) { mEmptyView.setVisibility(View.VISIBLE); setVisibility(View.GONE); } else { // If the caller just removed our empty view, make sure the list view is visible setVisibility(View.VISIBLE); } // We are now GONE, so pending layouts will not be dispatched. // Force one here to make sure that the state of the list matches // the state of the adapter. if (mDataChanged) { this.onLayout(false, mLeft, mTop, mRight, mBottom); } } else { if (mEmptyView != null) mEmptyView.setVisibility(View.GONE); setVisibility(View.VISIBLE); } }
ListView添加删除更新header和footer
对footer和header的操作相同,这里以header为例。其实是完全相同的,最终都被封装为FixedViewInfo,只是显示的位置不同
ListView可以添加多个header
TextView header = new TextView(this);header.setText("this is header 1");listview.addHeaderView(header);header = new TextView(this);header.setText("this is header 2");listview.addHeaderView(header);header和footer连同adapter被包装在了HeaderViewListAdapter中,
header和footer相关view保存在了下面两个方法中
ArrayList<ListView.FixedViewInfo> mHeaderViewInfos;ArrayList<ListView.FixedViewInfo> mFooterViewInfos;HeaderViewListAdapter并没有提供获取FixedViewInfo的方法,因此无法获取FixedViewInfo的实例。
如果外部没有header的引用,就无法获取这个header了。这个时候要更新header就需要先移除,然后重新创建,重新添加。
重新创建是很浪费内存的,如果header的数据要更新,还是在外部持有引用直接更新比较好。
header的内容不是原始adapter中的数据,header数据变化不需要通知数据更新。外部持有引用可直接修改内容,不需要通知adapter刷新数据。
listView中多种类型布局使用
BaseAdapter中默认是一种类型布局,支持多种类型布局需要在自定义的Adapter中重写这两个方法
public int getViewTypeCount() { return 1; } public int getItemViewType(int position) { return 0; }
getViewTypeCount返回布局类型个数。getItemViewType返回某个位置的布局类型。
这个地方容易出现数组越界的问题。getItemViewType的返回值是不可以随便写的。它的最大值不超过TypeCount-1,
否则就会数组越界。
这是因为在复用多种布局时,TypeCount是布局类型最大个数,而ItemViewType是一个下标,
这个下标的最大值就是TypeCount-1。
/** * Unsorted views that can be used by the adapter as a convert view. */ private ArrayList<View>[] mScrapViews; public void setViewTypeCount(int viewTypeCount) { if (viewTypeCount < 1) { throw new IllegalArgumentException("Can't have a viewTypeCount < 1"); } //noinspection unchecked ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount]; for (int i = 0; i < viewTypeCount; i++) { scrapViews[i] = new ArrayList<View>(); } mViewTypeCount = viewTypeCount; mCurrentScrap = scrapViews[0]; mScrapViews = scrapViews; }
View getScrapView(int position) { final int whichScrap = mAdapter.getItemViewType(position); if (whichScrap < 0) { return null; } if (mViewTypeCount == 1) { return retrieveFromScrap(mCurrentScrap, position); } else if (whichScrap < mScrapViews.length) { return retrieveFromScrap(mScrapViews[whichScrap], position); } return null; }
OnItemClickListener中获取获取点击的item数据
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
这个方法的postiton并不是真实的item数据位置,当没有header时,positon与item位置对应
当有header时,positon=headercount+item位置,也就是说
item位置=positon-headercount
listview.getHeaderViewsCount()可以获取headercount
还有一种方法是通过包装过的adapter直接获取
parent.getAdapter().getItem(position);
异步加载图片混乱
当滑动ListView的时候,图片自动变来变去,图片显示的位置也不正确。
所有的主流图片加载库都解决了这个问题。这个问题的原因是listview的item复用。
解决这个问题的思路都是建立当前imageview与加载的url的对应关系,使当前看到的item始终加载的是当前对应数据。
Picasso通过建立ImageView与ImageViewAction的的对应关系来加载正确图片
void enqueueAndSubmit(Action action) { Object target = action.getTarget(); if (target != null && targetToAction.get(target) != action) { // This will also check we are on the main thread. cancelExistingRequest(target); targetToAction.put(target, action); } submit(action); }
RecycleBin机制
View复用方式
public static final class ViewHolder { public ImageView img; public TextView name; public ViewHolder(View convertView) { img = (ImageView) convertView.findViewById(R.id.img); name = (TextView) convertView.findViewById(R.id.name); } }
@Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if (convertView == null) { convertView = LayoutInflater.from(ListViewActivity.this).inflate(R.layout.item_list, parent, false); viewHolder = new ViewHolder(convertView); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.name.setText(data.get(position)); return convertView; }
源码解析
单独再做分析吧
ListView中的设计模式
ListView与Adapter的应用就是典型的适配器模式
ListView与Adapter都实现了接口ListAdapter,同时ListView又包裹了Adapter,从而实现适配,是典型的对象适配。
装饰模式
HeaderViewListAdapter是典型的装饰模式
对Adapter进行装饰,为Adapter扩展了footer和header
数据刷新观察者模式
在设置Adapter时会构建一个AdapterDataSetObserver,这就是创建观察者
adapter中包含一个数据集可观察者DataSetObservable,这就是可观察者
adapter的notifyDataSetChanged就是可观察者通知观察者数据发生了变化。
其它常见小问题:
点击item没反应
原因是tem子控件获取了焦点,在Item布局的根布局加上android:descendantFocusability=“blocksDescendants”
的属性就可以了。
descendantFocusability值含义:
beforeDescendants:viewgroup会优先其子类控件而获取到焦点
afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点
blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点
给ListView加上背景图片,或者背景颜色时,滚动时listView会黑掉
原因是,滚动时,列表里面的view重绘时,用的依旧是系统默认的透明色,颜色值为#FF191919,
要改变这种情况,只需要调用listView的setCacheColorHint(0),颜色值设置为0
或者xml文件中listView的属性 Android:cacheColorHint="#00000000"即可。
ListView设置item高度无效
在item的layout文件中,用android:layout_height设置item的高度。运行,高度设置无效。
解决办法:给item设定minHeight,即可。
设置虚线分割线
android:divider="@drawable/dash_line"dash_line为drawable
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="line"> <!-- 显示一条虚线,破折线的宽度为dashWith,破折线之间的空隙的宽度为dashGap,当dashGap=0dp时,为实线 #d6dadd --> <stroke android:width="0.5dp" android:color="#D6DADD" android:dashGap="2dp" android:dashWidth="2dp" /></shape>
欢迎扫描二维码,关注公众号
- ListView注意的问题和源码解析(上)
- android源码解析--ListView(上)
- ListView实现下拉刷新和上拉加载更多时遇到的诸多问题与解析
- ListView需要注意的问题
- ListView 优化注意的问题
- 有关ListView注意的问题
- Scrollview和listview,gridview嵌套应注意的问题
- 王学岗ListView和源码解析(一)
- 王学岗ListView和源码解析(二)
- 王学岗ListView和源码解析(三)
- ListView应该注意的一些问题
- ListView(三)需要注意的问题
- Listview中一些小的注意问题
- ListView---一个神奇的控件源码解析
- android源码解析--ListView
- android源码解析--ListView
- ListView源码解析
- android listview源码解析
- 讯飞语音开发之语音听写--不带ui界面
- css鼠标样式
- SQL语句创建相同结构的表
- AFNetworking迁移到3.1.0
- UIScrollView之Zoom
- ListView注意的问题和源码解析(上)
- Android开发短信发送
- Android中的AOP编程
- PAT乙级—1018. 锤子剪刀布 (20)-native
- IT忍者神龟之长连接与长轮询分别如何实现的?各有哪些优势和劣势?
- leetcode2 add two number
- 矩阵连乘问题 DP
- Rsa加密解密
- 你知道为什么下载的HTML5模版打开非常缓慢?