小技巧:解决item项不能被选中的问题
问题:当点击某-行的时候没有背景色的变化
原因:行中包含了按钮,按钮抢夺了焦点
解决方案:夺回焦点
在行布局中设置descendantFocusability
属性
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:descendantFocusability="blocksDescendants">
方法一(简单的应用Arraydapter)
public class ListViewActivity extends AppCompatActivity r{
private ListView listView;
private String[] data = {
"Apple","banana","orange","watermelon","pear","grape","cherry"
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
initView();
}
private void initView() {
listView = findViewById(R.id.list_view);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(ListViewActivity.this,R.layout.listview_item,R.id.tv,data);
listView.setAdapter(adapter);
}
}
这里使用的适配器为ArrayAdapter。又因为我们提供的数据都是字符串类型的数据,所以这里适配器的泛型指定为String。其中ArrayAdapter的构造函数中依次传入的参数为 上下文、listview的布局样式(这里使用用系统自带的)、data是上面提供的数据
listView.setAdapter(adapter);
上式用于将构建好的适配对象传递进去,这这样ListView和数据间的关联就建立完成了
方法二
Arraydapter一般用于简单的文字列表显示,也可以用于图文显示listview,但图文显示一般用SimpleAdapter来实现
public class ListView1Activity extends Activity {
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
initView();
}
private void initView() {
//获取listview对象
listView = findViewById(R.id.list_view);
//准备数据源
List<Map<String,Object>> list = new ArrayList<Map<String, Object>>();
Map<String,Object> map = new HashMap<String, Object>();
map.put("logo",R.mipmap.ic_launcher);
map.put("text","1");
list.add(map);
map =new HashMap<String, Object>();//每次重新new一个是为了开启新的iteem
map.put("logo",R.mipmap.ic_launcher);
map.put("text","2");
map =new HashMap<String, Object>();
map.put("logo",R.mipmap.ic_launcher);
map.put("text","3");
map =new HashMap<String, Object>();
map.put("logo",R.mipmap.ic_launcher);
map.put("text","4");
//准备适配器Adapter
SimpleAdapter simpleAdapter = new SimpleAdapter(
this,
list,
R.layout.listview_item,
new String[] {"logo"
,"text"},
new int[]{
R.id.iv,
R.id.tv
}
);
//将适配器关联到ListView
listView.setAdapter(simpleAdapter);
}
}
方法三:自定义Adapter
public class MyListViewActivity extends Activity {
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
initView();
}
private void initView() {
//获取listview对象
listView = findViewById(R.id.list_view);
//准备数据源
List<Map<String,Object>> list = new ArrayList<Map<String, Object>>();
Map<String,Object> map = new HashMap<String, Object>();
map.put("logo",R.mipmap.ic_launcher);
map.put("text","1");
list.add(map);
map =new HashMap<String, Object>();//每次重新new一个是为了开启新的iteem
map.put("logo",R.mipmap.ic_launcher);
map.put("text","2");
list.add(map);
map =new HashMap<String, Object>();
map.put("logo",R.mipmap.ic_launcher);
map.put("text","3");
list.add(map);
map =new HashMap<String, Object>();
map.put("logo",R.mipmap.ic_launcher);
map.put("text","4");
list.add(map);
//准备适配器Adapter
MyAdapter myAdapter = new MyAdapter(this);
myAdapter.setList(list);
//将适配器关联到ListView
listView.setAdapter(myAdapter);
}
public class MyAdapter extends BaseAdapter {
//数据集合/,先声明一个list成员变量,用于接收传进来的数据
List<Map<String,Object>> list;
//反射器,也是作为一个成员变量
LayoutInflater inflater;
//构造器,方便再new对象时进行初始化工作,而参数context(上下文)是为了给反射器提供一个实例化对象
public MyAdapter(Context context){
inflater = LayoutInflater.from(context);//用于传入上下文,因为这个不想活动一样有引用上下文的操作,所以需要自己加
}
//setList是为了将数据传入进来(传入数据集合)
public void setList(List<Map<String, Object>> list) {
this.list = list;
}
//获取当前listview的行数的
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
//getView方法就是显示ListView的一行的加载逻辑
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//反射器,将布局文件加载进来
View view = inflater.inflate(R.layout.listview_item,null);
ImageView iv=view.findViewById(R.id.iv);
TextView tv = view.findViewById(R.id.tv);
Map map = list.get(position);
iv.setImageResource((Integer)map.get("logo"));
tv.setText((String)map.get("text"));
return view;
}
}
ListView的点击事件和长按事件
listView.setOnItemClickListener(this);
listView.setOnItemLongClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(MyListViewActivity.this,"点击了"+position,Toast.LENGTH_SHORT).show();
}
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(MyListViewActivity.this,"长按了"+position,Toast.LENGTH_SHORT).show();
return true;
//return false表示事件不被消化,即长按后会默认出发点击事件,若为true,则事件在长按事件触发后就被消化,不在触发点击事件
}
Selector背景选择器
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 控件被按压时(此项true时包含了下面的两项focused\selected)-->
<item android:drawable="@drawable/abc_vector_test" android:state_pressed="true"/>
<!-- 控件是否获得焦点(控件被压住时也就获得了焦点)-->
<item android:drawable="@drawable/abc_vector_test" android:state_focused="true"/>
<!-- 控件是否被选中-->
<item android:drawable="@drawable/abc_vector_test" android:state_selected="true"/>
<!-- 控件是否可用(控件不可用时处于灰色不可点击)-->
<item android:drawable="@drawable/abc_vector_test" android:state_enabled="true"/>
<item android:drawable="@drawable/abc_vector_test" android:state_checked="true"/>
</selector>
控件引用android:background="@drawable/selector"
ListView控件引用:android:listSelector="@drawable/selector"
ListView的适配器优化
在适配器的getView()
方法中,每次加载一个item都会重新加载一次布局,这就导致listview快速滚动时,这就会成为一个性能的瓶颈。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = inflater.inflate(R.layout.listview_item,null);
又可以看到getView()
方法还有个convertView
参数,这个参数用于将之前加载好的布局进行缓存,以便之后还可以重用。
由此可以进行优化:
View view;
if (convertView == null){
view = inflater.inflate(R.layout.listview_item,null);
}else {
view = convertView;
}
除此之外还可以对view获取控件实例这一块进行优化,
我们可以自定义一个内部类来优化这块内容;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
ViewHolder viewHolder;
if (convertView == null){
view = inflater.inflate(R.layout.listview_item,null);
viewHolder = new ViewHolder();
viewHolder.iv=view.findViewById(R.id.iv);
viewHolder.tv=view.findViewById(R.id.tv);
view.setTag(viewHolder);//将viewHodler的对象存储在view中
}else {
view = convertView;
viewHolder = (ViewHolder) view.getTag();//重新获取ViewHolder
}
Map map = list.get(position);
viewHolder.iv.setImageResource((Integer) map.get("logo"));
viewHolder.tv.setText((String) map.get("text"));
return view;
}
class ViewHolder{
ImageView iv;
TextView tv;
}
上面的还可以将View view删除,直接参数convertView:
public class MyAdapter extends BaseAdapter {
private Context mContext;
//数据集合/,先声明一个list成员变量,用于接收传进来的数据
List<Map<String,Object>> list;
//反射器,也是作为一个成员变量
LayoutInflater inflater;
//构造器,方便再new对象时进行初始化工作,而参数context(上下文)是为了给反射器提供一个实例化对象
// public MyAdapter(Context context){
// inflater = LayoutInflater.from(context);//用于传入上下文,因为这个不想活动一样有引用上下文的操作,所以需要自己加
// }
public MyAdapter(Context context, List<Map<String, Object>> list) {
this.mContext = context;
this.list = list;
}
// //setList是为了将数据传入进来(传入数据集合)
// public void setList(List<Map<String, Object>> list) {
// this.list = list;
// }
//获取当前listview的行数的
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
//getView方法就是显示ListView的一行的加载逻辑
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//反射器,将布局文件加载进来
ViewHolder viewHolder;
if (convertView == null){
convertView = LayoutInflater.from(this.mContext).inflate(R.layout.listview_item,null);
viewHolder = new ViewHolder();
viewHolder.iv=convertView.findViewById(R.id.iv);
viewHolder.tv=convertView.findViewById(R.id.tv);
convertView.setTag(viewHolder);//将viewHodler的对象存储在view中
}else {
viewHolder = (ViewHolder) convertView.getTag();//重新获取ViewHolder
}
Map map = list.get(position);
viewHolder.iv.setImageResource((Integer)map.get("logo"));
viewHolder.tv.setText((String)map.get("text"));
return convertView;
}
class ViewHolder{
ImageView iv;
TextView tv;
}
}
活动处代码修改
//准备适配器Adapter
MyAdapter myAdapter = new MyAdapter(this,list);
// myAdapter.setList(list);
//将适配器关联到ListView
listView.setAdapter(myAdapter);