"ListView "-Android面试必问"精华技能点"汇总.
来源:互联网 发布:c语言a =b 编辑:程序博客网 时间:2024/06/12 01:31
ListView
目录:
- ListView如何提高效率
- ViewHolder为什么要声明为静态类
- 在Activity中如何使用Handlder去除警告消息
- 谈谈ListView中的MVC思想
- ListView使用了哪些设计模式
- 当ListView数据集改变之后如何更新ListView
- ListView如何实现分页加载
- ListView可以显示多类型的条目吗
- ListView如何定位到指定位置
- 如何在ScrollView中嵌套ListView
- ScrollView嵌套listView方法除了设置高度外还有什么方法
- ListView中如何优化图片
- ListView中图片错位问题是如何产生的怎么解决
1.ListView如何提高效率?
- 1.复用convertView
- 2.使用静态的ViewHolder
- 3.使用弱引用WeakReference引用View对象
- 4.采用分页加载(比如上拉加载更多)
效果图:
(PS:如果ListView里面的Item里设置了按钮,那么按钮可点击,item不能点击,解决方法:Buttton声明参数:android:focusable=”false”)
代码案例如下
activity_main.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:orientation="vertical" android:layout_height="match_parent"> <ListView android:id="@+id/lv" android:layout_width="match_parent" android:layout_height="match_parent"> </ListView></LinearLayout>
layout_item.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:orientation="horizontal" android:weightSum="7" android:layout_height="wrap_content"> <ImageView android:id="@+id/iv" android:src="@drawable/ic_launcher" android:layout_weight="2" android:layout_width="0dp" android:layout_height="80dp"/> <LinearLayout android:layout_marginTop="10dp" android:orientation="vertical" android:layout_width="0dp" android:layout_weight="3" android:layout_height="match_parent"> <TextView android:id="@+id/name" android:textSize="20sp" android:text="Name" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:singleLine="true" android:textSize="18sp" android:text="Num : 123412344" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:singleLine="true" android:textSize="17sp" android:text="Time : 2016-8-8 8:11" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> <Button android:id="@+id/bt" android:layout_width="0dp" android:layout_weight="2" android:textSize="20sp" android:text="拨打电话" android:focusable="false" android:layout_height="match_parent"/></LinearLayout>
MainActivity.java
package goodjobtome.com.viewtest;import android.app.Activity;import android.graphics.BitmapFactory;import android.os.Bundle;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.ListView;import android.widget.TextView;import java.lang.ref.WeakReference;public class MainActivity extends Activity { private ListView mLv; private MyAdapter mAdapter; private String[] mNames; private int[] mPics; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } private void init() { initView(); initData(); initEvent(); } private void initView() { mLv = (ListView) findViewById(R.id.lv); } private void initData() { mAdapter = new MyAdapter(); mNames = new String[]{"一","二","三","四","五","六","七","八","九","十","十一","十二"}; mPics = new int[]{R.mipmap.n1,R.mipmap.n2,R.mipmap.n3,R.mipmap.n4,R.mipmap.n5,R.mipmap.n6, R.mipmap.pre1,R.mipmap.pre2,R.mipmap.pre3,R.mipmap.pre4,R.mipmap.pre5,R.mipmap.pre6}; mLv.setAdapter(mAdapter); } private void initEvent() { } class MyAdapter extends BaseAdapter { //决定listView一共有多少个item @Override public int getCount() { return mPics.length; } //可以不写,不是在base中调用,可以直接返回null @Override public Object getItem(int position) { return null; } //返回的是position对应的item的id,直接返回position @Override public long getItemId(int position) { return position; } //返回条目 @Override public View getView(int position, View convertView, ViewGroup parent) { WeakReference<ImageView> weakIv; ViewHolder holder = null; //刚开始为空先定义填充item,convertView if (convertView == null) { System.out.println(position+" 位置还未复用"); holder = new ViewHolder(); convertView = View.inflate(MainActivity.this, R.layout.layout_item, null); holder.tv = (TextView) convertView.findViewById(R.id.name); //holder.iv = (ImageView) convertView.findViewById(R.id.iv); holder.bt = (Button) convertView.findViewById(R.id.bt); /*--------------采用弱引用,引用ImvageView对象--------------*/ ImageView iv = (ImageView) convertView.findViewById(R.id.iv); WeakReference<ImageView> weakIv = new WeakReference<ImageView>(iv); holder.iv = weakIv.get(); //给convertView设置标记 convertView.setTag(holder); } else { //复用了之后,holder = convertView设置标记 holder = (ViewHolder) convertView.getTag(); System.out.println(position+" 位置复用"); } holder.tv.setText(mNames[position]); holder.iv.setImageBitmap(BitmapFactory.decodeResource(getResources(), mPics[position])); return convertView; } } //静态viewHolder,避免了对外部的引用 static class ViewHolder { TextView tv; ImageView iv; }}
2.ViewHolder为什么要声明为静态类?
- 一般的(非static)内部类对外部类具有强引用
- 为了避免对外部类(Activity)对象的引用,所以才声明.
3.在Activity中如何使用Handlder去除警告消息?
解决方法: static内部类+弱引用:
我们一般使用Handler都会写成一下形式,然后AndroidStudio就会提出警告(一大片被颜色渲染)
或者如图(警告):
翻译过来是:这个handler类应该为”静态”,否则可能内存泄露.解释:
- 执行了Activity的finish,但是被延迟处理还未处理的消息包含对Handler的引用.
- Handler是“匿名内部类”,持有外部的Activity的引用
- 导致Activity无法回收,很多资源都无法回收,产生了内存泄露
解决
- 静态内部类不对外部持有引用,所以定义成静态类的handler
- 加上个static警告就消失了
- 所要想解决全部问题,就还需要使用”弱引用”.
- 官方给出的建议写法:
- 内部类声明弱引用<引用外部类>对象
- 内部类构造时创建”弱引用<引用外部类>”对象
- 内部类的方法通过弱引用获取外部类对象,进行判断非空再操作
class OuterClass { class InnerClass { private final WeakReference<OuterClass> mTarget; InnerClass(OuterClass target) { mTarget = new WeakReference<OuterClass>(target); } void doSomething() { OuterClass target = mTarget.get(); if (target != null) { target.do(); } }}
所以根据介绍我们改进handler为:
static class myHandler extends Handler { //弱引用<引用外部类> WeakReference<Activity> mActivity; myHandler(Activity activity) { //构造创建弱引用 mActivity = new WeakReference<Activity>(activity); } @Override public void handleMessage(android.os.Message msg) { //通过弱引用获取外部类. Activity activity = mActivity.get(); //进行非空再操作 if (activity != null) { //doSomething } } }
完美解决问题.
4.谈谈ListView中的MVC思想?
M: model
数据模型,比如里面的图片和名字资源.
V: view
显示布局,item和listView.
C: controller,控制器
控制器,比如适配器和调用的方法.
5.ListView使用了哪些设计模式?
- 适配器模式
- 观察者模式
- 享元模式
6.当ListView数据集改变之后,如何更新ListView?
- 调用ListView的adapter的notifyDatasetChange()方法,即可重新绘制
- 比如需求:我点击按钮,要改变图片和名字
代码如下:
//点击事件holder.bt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "update", Toast.LENGTH_SHORT).show(); mNames[position] = "update"; mPics[position] = R.mipmap.ic_launcher; //适配器更新方法 mAdapter.notifyDataSetChanged(); }});
效果如下:
点击之后:
7.ListView如何实现分页加载?
① ListView 对象设置滚动监听,要求重写两个方法:
滚动状态发生变化的方法(onScrollStateChanged),有三种状态:
- //1:手指按下触摸滑动:SCROLL_STATE_TOUCH_SCROL,对应1
- //2.滑翔:SCROLL_STATE_FLING,对应2
- //3.静止:SCROLL_STATE_IDLE,对应0
- listView 被滚动时调用的方法(onScroll)
分页处理逻辑:
- 1.只关心静止状态:关心最后一个可见的条目
- 2.如果最后一个可见条目就是数据适配器(集合)里
的最后一个 - 此时即加载更多的数据。
- 每次加载的时,计算出滚动的数量
- 当滚动的数量大于等于总数量的时,提示无更多数据。
代码如下:
private void initEvent() { //滚动监听 mLv.setOnScrollListener(new AbsListView.OnScrollListener() { //滚动状态改变的方法 @Override public void onScrollStateChanged(AbsListView view, int scrollState) { //打印状态:触摸,静止 Log.d("AAA", "scrollState:" + scrollState); //1:手指按下触摸滑动:SCROLL_STATE_TOUCH_SCROL,对应1 //2.滑翔:SCROLL_STATE_FLING,对应2 //3.静止:SCROLL_STATE_IDLE,对应0 } //滚动时的方法 @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { //重点是:最后一个可见条目 //firstVisibleItem : 第一个可见条目 //visibleItemCount : 当前可见条目总数 //totalItemCount : 存在的条目总数 //通过第一个可见+可见总数就能得到最后显示的了 Log.d("last View", firstVisibleItem + visibleItemCount + ""); int lastView = firstVisibleItem + visibleItemCount; if (lastView == mNames.length) { Toast.makeText(MainActivity.this, "最后一个项目到了,要加载更多数据了", Toast.LENGTH_SHORT).show(); } } }); }
打印结果:
07-07 08:30:07.216 8985-8985/? D/last View: 9 07-07 08:30:07.224 8985-8985/? D/last View: 9 07-07 08:30:07.236 8985-8985/? D/last View: 9 07-07 08:30:07.260 8985-8985/? D/last View: 10 07-07 08:30:07.284 8985-8985/? D/last View: 11 07-07 08:30:07.296 8985-8985/? D/last View: 11 07-07 08:30:07.316 8985-8985/? D/last View: 12
拉倒最后一个
显示效果:
8.ListView可以显示多类型的条目吗?
- 可以
- 原则上可以让每个条目设置不同的类型,通过getItem()方法就能获取条目
- adapter里还提供两个方法
- 获取条目类型数量(有多少种类型):getTypeCount
- 获取条目指定位置的条目类型:getItemViewType(position)
- 可以在getView方法里根据viewtype进行相应的加载.
9.ListView如何定位到指定位置?
- 用ListView 控件的.设置选择(位置):lv.setSlection(0);
- 比如刚刚的点击按钮后跳到指定位置
//点击事件holder.bt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "update", Toast.LENGTH_SHORT).show(); mNames[position] = "update"; mPics[position] = R.mipmap.ic_launcher; //适配器更新方法 mAdapter.notifyDataSetChanged(); //点击后定位到指定位置,lv设置选择位置 mLv.setSelection(0); }});
结果:每次点击后都跳到首位;
10.如何在ScrollView中嵌套ListView?
(PS:1.一般不嵌套,因为两个控件都能滚动,会冲突,所以我们要进行另外的处理)
(最快方法)设置ListView高度的方法:
- 1.必须在ScrollView里边设置线性布局LinearLayout.
- 2.ListVeiw放到线性布局里
- 3.必须给ListView 设置高度,否则还是只显示一行
布局代码如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:gravity="center" android:background="#ca6363" android:text="我是标题" android:textSize="30dp" android:layout_width="match_parent" android:layout_height="wrap_content"/> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/lv" android:layout_width="match_parent" android:layout_height="300dp"> </ListView> </LinearLayout> </ScrollView></LinearLayout>
显示效果:
11.ScrollView嵌套listView方法除了设置高度外还有什么方法?
一.测量高度方法
(效果:下拉完所有的条目才显示其他布局,当listView数据少的时候就少)
(原理就是测出ListView所有条目的高度),然后给布局参数赋值,ListView设置参数即可.
* 1.循环条目的数量(适配器获取条目里的view),获取view
* 2.测量view(0,0)
* 3.得到总高度
* 2.获取布局参数
* 3.给参数赋值
* 4.ListView设置布局参数
代码如下
/*--------------测绘高度--------------*/ListAdapter adapter = mLv.getAdapter();int totalHeight = 0;for (int i = 0; i < mAdapter.getCount(); i++) { //获取每个位置的view View view =mAdapter.getView(i, null, mLv); view.measure(0, 0); int measuredHeight = view.getMeasuredHeight(); totalHeight += measuredHeight;}//获取布局参数对象ViewGroup.LayoutParams params = mLv.getLayoutParams();//赋值参数params.height = totalHeight;//如果有分割线那么就加上所有分割线的高度//params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));//设置参数mLv.setLayoutParams(params);
效果如下:
可以改一下修改
更改scrollView的高度为固定,且button往外面.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:gravity="center" android:background="#ca6363" android:text="我是标题" android:textSize="30dp" android:layout_width="match_parent" android:layout_height="wrap_content"/> <ScrollView android:layout_width="match_parent" android:layout_height="300dp"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/lv" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout> </ScrollView> <Button android:layout_width="match_parent" android:layout_height="wrap_content"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content"/></LinearLayout>效果:
二.用单个ListView取代ScrollView
* 把scrollView里的所有内容,都放到ListView里,都做为ListView的一个条目,为此大家请看另一个图:
* 然后适配器的getView方法,通过position来决定具体填充哪一个布局:
总的布局就一个lv,等下的上下靠填充
逻辑图:
代码如下:
布局:
<ListView android:id="@+id/act_solution_2_lv" android:layout_width="fill_parent" android:layout_height="wrap_content"></ListView>
上下靠填充:
逻辑代码:
public View getView(int position, View convertView, ViewGroup parent) { //条目第一个位置填充的顶部布局 if(position == 0){ convertView = inflater.inflate(R.layout.item_solution2_top, null); return convertView; } //条目第二个填充的ListView else if(position == 21){ convertView = inflater.inflate(R.layout.item_solution2_bottom, null); return convertView; } //条目第三项填充的底部布局 ViewHolder h = null; if(convertView == null || convertView.getTag() == null){ convertView = inflater.inflate(R.layout.item_listview_data, null); h = new ViewHolder(); h.tv = (TextView) convertView.findViewById(R.id.item_listview_data_tv); convertView.setTag(h); }else{ h = (ViewHolder) convertView.getTag(); } h.tv.setText("第"+ position + "条数据"); return convertView;}
三.用LinearLayout取代ListView
(PS:这个已经是替代了可能偏题了,但起码还算个方法)
逻辑就是: 建立一个类继承LinearLayout,然后为他加上对BaseAdapter的适配.然后把ListView改成线性布局.
最终的效果是,每个布局加一个条目而已.
四.自定义ListView来适应ScrollView
(PS:逻辑就是:一个类继承ListView,然后重写onMeasure方法,达到对ScrollView的适配),核心逻辑就是第一种方法的测绘而已.
总结:第一种第二种就能解决问题,面试时就聊测绘方法,还有第二种(统一ListVeiw)自定义每个条目的方法就好了.
11.ListView中如何优化图片?
(PS:ListView里的条目如果涉及到大量的图片,那么一定要对图片进行处理,说白了核心就是为了内存着想)
- 1.对于大图片,不要直接拿到路径就去循环解析*Bitmap.decodeFile(),不要直接加载到内存*
- 应该进行图片边界压缩,除非后台服务器的图片都是(优化过匹配的)
- Option保持图片大小
- 应该进行图片边界压缩,除非后台服务器的图片都是(优化过匹配的)
- 在ListView中取图片的时候而不要直接取,应用用弱引用代替强引用
- getView中图片转换的时候,变量要及时转换
- 在ListView中取图片的时候而不要直接取,应用用弱引用代替强引用
- 3.异步加载图片思想(框架):
- 1.采用线程池
- 2.采用三级缓存.
12.ListView中图片错位问题是如何产生的?怎么解决?
一.产生的原因:
- 1.因为我们采用的是缓存复用convertView
- 2.比如屏幕能显示10个item,当第一个item显示完,但是第11个因网络问题延迟了,获取数据慢了发现没有图片资源,就会显示它所重用的convertView 原来显示的图片.
- 3.导致当加载获取到图片的时候,item已经不在该位置了.
- 4.导致可能在第十几个条目显示图像
二.错乱分类:
1. 行item图片显示重复
这个显示重复是指当前行item显示了之前某行item的图片。
比如ListView滑动到第2行会异步加载某个图片,但是加载很慢,加载过程中listView已经滑动到了第14行,且滑动过程中该图片加载结束,第2行已不在屏幕内,根据上面介绍的缓存原理,第2行的view可能被第14行复用,这样我们看到的就是第14行显示了本该属于第2行的图片,造成显示重复。2. 行item图片显示错乱
这个显示错乱是指某行item显示了不属于该行item的图片。
比如ListView滑动到第2行会异步加载某个图片,但是加载很慢,加载过程中listView已经滑动到了第14行,第2行已不在屏幕内,根据上面介绍的缓存原理,第2行的view可能被第14行复用,第14行显示了第2行的View,这时之前的图片加载结束,就会显示在第14行,造成错乱。3. 行item图片显示闪烁
上面b的情况,第14行图片又很快加载结束,所以我们看到第14行先显示了第2行的图片,立马又显示了自己的图片进行覆盖造成闪烁错乱。
二.解决的办法:
(PS:加上判断和提示解决)
If (!t == null){mViewHolder.nameText.setText(t);}else{ mViewHolder.nameText.setText("unknow");}If(!p == null){mViewHolder.photoView.setImageBitmap(p);}else{ mViewHolder.photoView.setImageBitmap("显示提示的图片");}
这样避免了显示的错乱
- "ListView "-Android面试必问"精华技能点"汇总.
- "Android 布局"-面试必问"精华技能点"汇总.
- "Intent"-Android面试必问"精华技能点"汇总
- "Fragment"-Android面试必问"精华技能点"汇总
- "Android中的访问网络"-Android面试必问"精华技能点"汇总
- "Android 性能优化"-Android面试必问"精华技能点"汇总
- "Android 屏幕适配"-Android面试必问"精华技能点"汇总
- "Android Touch事件分发机制"-Android面试必问"精华技能点"汇总
- "Android中的动画"-Android面试必问"精华技能点"汇总
- "Android自定义控件"-Android面试必问"精华技能点"汇总
- "Android-事件处理机制"之面试必问技能点汇总
- "Activity"-安卓面试必问技能点大总结"
- "Service"-安卓面试必问技能点大总结"
- "BroadcastReceiver"-安卓面试必问技能点大总结"
- Android面试必问的Listview getview方法问题
- android面试技能点
- 面试必问
- 面试必问
- 设计模式-桥接模式
- JavaEE学习笔记之Servlet/JSP(2)
- Quartz
- LintCode_130 Heapify
- nginx使用proxy_pass反向代理时cookie丢失问题解决方案
- "ListView "-Android面试必问"精华技能点"汇总.
- HDU 5531(Rebuild- 三分)
- qmake Variable Reference
- 狮子搏兔 亦用全力
- 《信号与系统》01 信号运算
- 调用 WinSCP 下载远程服务器文件 Python
- Scala中的Typeclass模式实例-转载于BitTiger.io
- struts2源码分析及拦截器实现原理
- Apple WatchKit 初探