【
1、通过ContentView实现优化,减少创建View的次数
2、通过ViewHolder实现优化,减少遍历查找控件的次数
getView方法优化(ListView的优化,convertView的重用)
getView方法的运行特点:
1. 在列表初始显示时一次性运行n次,n的大小取决于初始显示时能够显示多少个item条目
注意:此处的显示指的是只要item的一点点边缘显示出来就算做1个
2. 每当由于上下滑动造成某条目移动出屏幕再重新回来的时候,该position位置的条目也会再次运行getView方法
优化处理的原因:
由于上方getView方法的运行特点,决定了同一位置的View对象会被多次重复创建,一旦
View对象里面存储的数据内容比较大,又多次重复创建,易引发OutOfMemorryError,内存溢出错误。未解决内存上的优化提出了getView优化的思想
优化处理的核心思想:
getView方法的参数二convertView,该参数的特点:
1. 系统自动传递过来的值
2. 当ListView初始显示是为null
3. 一旦ListView开始滑动,那么converView使用的就是被滑动出屏幕的View对象
ListView的效率优化
1、通过ContentView实现优化,减少创建View的次数
2、通过ViewHolder实现优化,减少遍历查找控件的次数
使用优化后最直观的效果:
假设getView方法来回滑动运行100次,如果没有使用优化,那么将在内存中创建100个View对象进行存储,但是一旦使用了优化,那么内存中可能只会创建10个或者5,6个View对象,达到了内存优化的效果
优化的实现步骤:
1. 判断convertView对象是否为null,如果为null,那么通过LayoutInflater对象进行初始化并且将convertView对象作为返回值返回值
此步骤实现了减少被返回的View对象的初始化次数
2. 创建一个类(通常此类名为ViewHolder),在该类中定义n条属性,
定义的属性取决于item的布局文件中有多少控件
3. 在getView方法中声明一个ViewHolder对象,在convertView为null的时候,进行初始化,并初始化holder中所有的控件属性,
4. 初始化完成之后通过convertView的setTag方法将holder对象整体的存储起来
第2,3,4步骤的目的是减少item中控件的初始化次数
5. 当convertView不为null时,通过getTag方法取出之前存储的ViewHolder对象
6. 通过holder调用属性设置控件的显示内容即可
】
【
public class Item {
privateint imageId;
privateString name;
privateString text;
publicint getImageId() {
returnimageId;
}
}
public class MainActivity extends Activity{
ListView lv;
ArrayList
int[]imgs = {R.drawable.pic0,R.drawable.pic1,R.drawable.pic2,R.drawable.pic3,R.drawable.pic4};
privateLayoutInflater inflater;
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv= (ListView)findViewById(R.id.lv);
for(int i=0;i<30;i++) {
list.add(newItem(imgs[i%imgs.length], "name"+i, "texxt"+i));
}
inflater= LayoutInflater.from(this);
//设置适配器
lv.setAdapter(newMyAdapter());
}
classMyAdapter extends BaseAdapter {
@Override
publicint getCount() {
//TODO Auto-generated method stub
returnlist.size();
}
@Override
publicObject getItem(int position) {
//TODO Auto-generated method stub
returnnull;
}
@Override
publiclong getItemId(int position) {
//TODO Auto-generated method stub
return0;
}
@Override
publicView getView(int position, View convertView, ViewGroup parent) {
//TODO Auto-generated method stub
ViewHolderhodler;
//1.判断convertView对象是否为null
if (convertView == null) {
Log.i("====","====== getview convertView isnull "+position );
//3.当convertView为null时初始化convertView对象,holder对象,以及holder中的所有属性
convertView= inflater.inflate(R.layout.item, null);
hodler = newViewHolder();
hodler.iv= (ImageView)convertView.findViewById(R.id.imageView1);
hodler.tv= (TextView)convertView.findViewById(R.id.textView1);
hodler.tv2= (TextView)convertView.findViewById(R.id.textView2);
//4.通过setTag方法将holder对象存入convertView对象的tag属性中
convertView.setTag(hodler);
}else {
Log.i("====","====== getview convertView"+position + convertView.hashCode());
//5.取出convertView对象的tag属性中之前存储好的holder对象进行重用
hodler = (ViewHolder) convertView.getTag();
}
Itemitem = list.get(position);
//6.通过holder对象取出对应的控件,设置显示即可
hodler.iv.setImageResource(item.getImageId());
hodler.tv.setText(item.getName());
hodler.tv2.setText(item.getText());
// Log.i("===","===== return view "+v.hashCode());
returnconvertView;
}
//2.自定义ViewHodler对象存储item布局中的所有控件
classViewHolder {
ImageViewiv;
TextViewtv;
TextViewtv2;
}
}
}
】
【
public class Student {
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
/**
* 实现练习需求如下:
* 先在ListView中显示10条学生数据,后期可以通过在输入框中输入内容后,点击添加按钮,实现数据的
* 添加,同时可以通过listview的item的点击事件实现学生数据的删除
*/
public class MainActivity extendsActivity {
ListView lv;
EditText et1;
EditText et2;
ArrayList
private StudentAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
for (int i=0;i<10;i++) {
list.add(new Student("name"+i, i+10));
}
adapter = new StudentAdapter();
lv.setAdapter(adapter);
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view,
int position, long id) {
// TODO Auto-generated method stub
list.remove(position);
adapter.notifyDataSetChanged();
}
});
}
private void initView() {
lv = (ListView)findViewById(R.id.listView1);
et1 = (EditText)findViewById(R.id.editText1);
et2 = (EditText)findViewById(R.id.editText2);
}
public void click (View v) {
//将数据添加到列表中进行显示
// list.add(new Student(n, a));
//如果想要让新添加的数据显示在指定位置
if( TextUtils.isEmpty(et1.getText().toString())&& TextUtils.isEmpty(et2.getText().toString())){
String n = et1.getText().toString();
int a =Integer.parseInt(et2.getText().toString());
list.add(0, new Student(n, a));
adapter.notifyDataSetChanged();
}else{
return;
}
}
class StudentAdapter extendsBaseAdapter {
@Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
}
@Override
public Object getItem(intposition) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(intposition) {
// TODO Auto-generated method stub
return 0;
}
@Override
public View getView(intposition, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder holder;
if(convertView == null) {
convertView = View.inflate(MainActivity.this, R.layout.item, null);
holder = new ViewHolder();
holder.name =(TextView)convertView.findViewById(R.id.textView1);
holder.age =(TextView)convertView.findViewById(R.id.textView2);
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
Student stu = list.get(position);
holder.name.setText(stu.getName());
holder.age.setText(stu.getAge()+"");
returnconvertView;
}
class ViewHolder {
TextView name;
TextView age;
}
}
}
】
【
ListView中常见bug
1. 当item中包含checkbox或者button这些具备默认点击效果的控件时,易引发的bug:
造成ListView无法接收ItemClick事件,
原因:checkbox或者button这些具备默认点击效果的控件默认抢占页面焦点
解决方式:使item具备焦点的能力
解决方式一:找到对应抢占焦点的控件,给该控件的标签添加一条android:focusable=“false”,让当前控件失去焦点
解决方式二:在item布局的根标签中添加属性:android:descendantFocusability="blocksDescendants" ,屏蔽根标签内所有子控件 的焦点
2. 当item中包含CheckBox控件时,如果想要实现CheckBox本身单独的点击事件,那么
只需在getView方法中通过holder对象直接调用CheckBox对象进行设置即可
3. 当item中包含CheckBox控件时,如果想要实现即使点击是CheckBox控件区域也算做listveiw的itemclick事件的话,
实现方式:在CheckBox标签中添加属性:android:clickable = false
4. 当item中包含CheckBox控件并且列表比较长,多次上下滚动后,CheckBox的选中效果会乱串
产生原因: 由于convertView的复用带来,新滑动入屏幕的item的View对象复用了之前CheckBox选中那一行的View对象
解决方式:
在处理checkbox的选中事件时,同时将选中内容通过一个集合对象存储起来,在getView中,通过当前行的显示内容与选中集合进行判断,符合条件的,让当前行的checkbox选中,反之,设置checkbox非选中
public class MainActivity extends Activity{
ListViewlv;
ArrayList
privateMyCheckAdapter adapter;
//用于存储所有选中数据
ArrayList
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv= (ListView)findViewById(R.id.lv);
for(int i=0;i<30;i++) {
list.add("item"+i);
}
adapter= new MyCheckAdapter(list,this,select);
lv.setAdapter(adapter);
lv.setOnItemClickListener(newOnItemClickListener() {
@Override
publicvoid onItemClick(AdapterView> parent, View view,
intposition, long id) {
//TODO Auto-generated method stub
Toast.makeText(MainActivity.this,"click !!",Toast.LENGTH_SHORT).show();
//手动处理CheckBox的选中效果
//取出被点击的那一行中的CheckBox对象
CheckBoxcb = (CheckBox) view.findViewById(R.id.checkBox1);
//取出点击时cb的当前效果,并根据当前效果将cb设置为相反效果
if(cb.isChecked()) {
cb.setChecked(false);
//根据checkbox的选中效果,针对select集合进行相应的操作
select.remove(list.get(position));
}else {
cb.setChecked(true);
//根据checkbox的选中效果,针对select集合进行相应的操作
select.add(list.get(position));
}
}
});
}
//实现点击button,同时删除多条选中内容的效果
publicvoid click (View v) {
list.removeAll(select);
adapter.notifyDataSetChanged();
select.clear();
}
}
】
【
实现的作用:
想要在不同类中针对必然会调用的同一个方法,实现不同的方法体处理代码
实现步骤:
1. 创建一个接口和一个抽象方法,方法的参数列表定义与onPostExecute方法的参数列表一致即可
2. 在异步任务类中,定义接口类型的变量,并且通过构造方法给该变量赋值
3. 在onPostExecute方法中通过接口对象调用抽象方法
4. 在对应Activity页面中实现上方接口重写相关方法,启动异步任务的时候通过构造方法传递该接口的实现子类
通过以上步骤即可实现,当在不同的页面中启动页面任务获取下载结果后,针对下载结果进行不同的操作
】
【
public class TextTask extendsAsyncTask
public interfaceLoadFinishListener {
void onLoadFinish(ArrayList
}
LoadFinishListenerani;
publicTextTask(LoadFinishListener ani) {
//TODO Auto-generated constructor stub
this.ani = ani;
}
@Override
protectedArrayList
//TODO Auto-generated method stub
Log.i("===","==== donin");
Stringjson = new HttpUtils().getJson(params[0]);
if(json != null) {
//连网成功,可以继续进行解析
returnnew JsonUtils().getList(json);
}
returnnull;
}
@Override
protected void onPostExecute(ArrayList
//TODO Auto-generated method stub
super.onPostExecute(result);
Log.i("===","==== result");
ani.onLoadFinish(result); //通过接口回调把解析后的数据对外提供
}
}
】
【自定义适配器:
1、定义类继承自BaseAdapter
2、改写getCount()方法:返回Item的数量
3、改写getItem()方法:返回当前Item对应的数据对象
4、改写getItemId()方法:返回当前Item的id
5、改写getView()方法:返回当前Item对应的View对象
1)通过布局填充器将指定的布局文件填转换成View对象
2)在View对象中查找相应的控件
3)设置各个控件的显示内容
4)效率优化
a、使用缓存View对象,减少创建View对象的次数
b、使用持有者模式,减少遍历查找控件的次数
5)返回View对象
】
【
去除标题栏和通知栏
通过设置主题实现
无标题主题:@android:style/Theme.Light.NoTitleBar
全屏主题:@android:style/Theme.Light.NoTitleBar.Fullscreen
通过代码实现
去除标题栏:Activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
去除通知栏:Activity.requestWindowFeature(Window.FEATURE_NO_TITLE);
PS:以上两行代码必须在setContentView(intresId)方式之前执行,不然会报错
】
【
设置滑动监听:setOnScrollListener(OnScrollListener)
OnScrollListener接口
onScrollStateChanged(AbsListViewview, int scrollState)
当滑动状态发生改变的时候回调的方法
参数1:view,被监听的对象
参数2:scrollState,当前手指在屏幕上滑动的状态
SCROLL_STATE_IDLE:停止滑动
SCROLL_STATE_TOUCH_SCROLL:手指正在屏幕上滑动
SCROLL_STATE_:惯性滑动
onScroll(AbsListView view, intfirstVisibleItem, int visibleItemCount, int totalItemCount)
当正在滑动的时候回调的方法
参数1:view,被监听的对象
参数2:firstVisibleItem,当前界面中第一个可见的item的下标
参数3:visibleItemCount,当前界面中可见的item的数量
参数4:totalItemCount,当前ListView中item的总数
当(firstVisibleItem + visibleItemCount =totalItemCount)时,表示ListView已滑到最底部
】
【
1、定义测试类继承自AndroidTestCase
2、在清单文件中添加
android:name="android.test.InstrumentationTestRunner" android:targetPackage="包名" >
3、在清单文件的application节点下添加
4、在测试类中定义测试方法,并使用AndroidJunit Test 运行指定的测试方法
】