前言:此篇是学习笔记,知识内容学习自:《第一行代码》、《android群英传》、《疯狂android讲义》。

使用基础ListView

ListView是最常用的控件之一,它以垂直列表的形式显示所有列表项,是比较难用好,也非常重要的。
ListView本身只是一个容器,而Adapter负责把内容添加到这个容器中,通过调用setAdapter()方法来实现。
基本使用的话很简单,第一步:在布局文件中加入ListView控件:

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.app.test.MainActivity">    <ListView  android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="match_parent" /></FrameLayout>

第二步:在Activity中调用setAdapter()给ListView添加内容:

public class MainActivity extends AppCompatActivity {    private ListView listView;    //列表内容data    private String[] data = new String[20];    //适配器    private ArrayAdapter<String> adapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        listView = (ListView) findViewById(R.id.listView);        //给data赋值        for (int i = 0; i < 20; i++) {            data[i] = "第" + i + "项";        }       //创建adapter,其中三个参数依次是:上下文,子布局id,内容        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data);        listView.setAdapter(adapter);    }}

这样就完成了!

自定义ListView界面

ListView的界面可以通过自定义布局来实现自定义的效果,接下来就来创建一个自定义ListView界面。
首先创建一个item的布局文件,我们仿造微信显示的内容。

<?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">    <ImageView  android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:src="@drawable/a" />    <LinearLayout  android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_weight="1" android:orientation="vertical">        <TextView  android:id="@+id/title" android:layout_width="match_parent" android:layout_height="30dp" android:gravity="center_vertical" android:paddingLeft="10dp" android:text="item1" />        <TextView  android:id="@+id/body" android:layout_width="match_parent" android:layout_height="30dp" android:gravity="center_vertical" android:paddingLeft="10dp" android:text="boooooooooody1" />    </LinearLayout></LinearLayout>

效果是这样的:

接着我们要新建一个Msg类用于管理item的信息:

public class Msg {    private int imageId;    private String title;    private String body;    public Msg(int imageId, String title, String body) {        this.imageId = imageId;        this.title = title;        this.body = body;    }    public int getImageId() {        return imageId;    }    public void setImageId(int imageId) {        this.imageId = imageId;    }    public String getTitle() {        return title;    }    public void setTitle(String title) {        this.title = title;    }    public String getBody() {        return body;    }    public void setBody(String body) {        this.body = body;    }}

非常简单,就是3个成员变量:图片的资源id,title,body,还有构造器和各自的get,set方法。
接着是自定义适配器,我们继承自BaseAdapter:

public class MyAdapter extends BaseAdapter {    private Context mContext;    private List<Msg> msgLsit;    private LayoutInflater inflater;    private ImageView imageView;    private TextView title;    private TextView body;    public MyAdapter(Context context, List<Msg> msgLsit) {        this.msgLsit = msgLsit;        mContext = context;        inflater = LayoutInflater.from(context);    }    @Override    public int getCount() {        return msgLsit.size();    }    @Override    public Object getItem(int position) {        return msgLsit.get(position);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        Msg msg = (Msg) getItem(position);        View view = inflater.inflate(R.layout.layout_item, null);        imageView = (ImageView) view.findViewById(R.id.imageView);        title = (TextView) view.findViewById(R.id.title);        body = (TextView) view.findViewById(R.id.body);        imageView.setImageResource(msg.getImageId());        title.setText(msg.getTitle());        body.setText(msg.getBody());        return view;    }}

重写了4个方法,重点看getView()这个方法,此方法会在子项被滚动到屏幕是调用,因此在这个方法里我们加载刚刚新建的子布局,并给控件附上内容。
最后就是在Activity中调用:

public class MainActivity extends AppCompatActivity {    private ListView listView;    private List<Msg> msgList = new ArrayList<Msg>();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        for (int i = 0; i < 20; i++) {            msgList.add(new Msg(R.drawable.a, "第" + i + "项", "内容:吧啦吧啦~"));        }        MyAdapter adapter = new MyAdapter(this, msgList);        listView = (ListView) findViewById(R.id.listView);        listView.setAdapter(adapter);    }}

看下效果:

性能优化

前面我们已经基本可以自由自在使用ListView了,但是那上述方法其实效率是很低下的。因为每次调用getView()方法就会去执行finViewById()方法,实际上我们只要调用一次就可以了。因此,我们可以使用ViewHolder来提高效率。
只需在我们自定义的adapter中加一个内部类ViewHolder,用来保存子布局的控件:

class ViewHolder {        private ImageView imageView;        private TextView title;        private TextView body;    }

然后修改getView()方法:

@Override    public View getView(int position, View convertView, ViewGroup parent) {        Msg msg = (Msg) getItem(position);        ViewHolder viewHolder;        if (convertView == null) {            viewHolder = new ViewHolder();            convertView = inflater.inflate(R.layout.layout_item, null);            viewHolder.imageView = (ImageView) convertView.findViewById(R.id.imageView);            viewHolder.title = (TextView) convertView.findViewById(R.id.title);            viewHolder.body = (TextView) convertView.findViewById(R.id.body);            convertView.setTag(viewHolder);        } else {            viewHolder = (ViewHolder) convertView.getTag();        }        viewHolder.imageView.setImageResource(msg.getImageId());        viewHolder.title.setText(msg.getTitle());        viewHolder.body.setText(msg.getBody());        return convertView;    }

这里的convertView是getView传进来的参数,用于将之前加载好的布局进行缓存,以便之后使用。第一次传进来的时候肯定是null,我们就用LanyoutInflater加载布局,然后调用setTag()保存viewHoler,第二次传进来就不是null了,因此我们可以直接使用。

分割线、滚动条、点击效果

布局文件中还可以设置一些其他属性,例如:

 <ListView        android:id="@+id/listView"        android:layout_width="match_parent"        android:layout_height="match_parent"        //设置分割线,可以是颜色,也可以是图片资源        android:divider="@android:color/holo_blue_dark"        //设置好分割线高度        android:dividerHeight="1dp"        //设置隐藏滚动条        android:scrollbars="none"        //设置点击效果(无)        android:listSelector="#00000000"        />

item定位

有些app的列表向上滑动时会有一个按钮,点击后可以回到顶部,其实用的就是ListView的一个方法,调用此方法可以将选定的item列为视图顶部。例如在上述第一个Activity中添加:

public class MainActivity extends AppCompatActivity {        ...//省略        listView.setAdapter(adapter);        listView.setSelection(10);    }}

再次运行后会发现是从第10项开始显示。

此方法是瞬间定位的,还有另外几个方法可以平滑地定位到指定位置。
还是上述Activity,添加一个Button和点击事件,调用listView的smoothScrollToPosition()方法,就可以实现平滑地定位到顶部:

button.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                listView.smoothScrollToPosition(0);            }        });

下列两个方法同样可以实现平滑定位:
smoothScrollByOffset(int offset);
smoothScrollBy(int distance,int duration);
可以自己尝试下,看看效果。

动态修改ListView内容

ListView中已经显示的内容,在某些情况下可能需要发生变化,如果通过重新设置adapter来更新,这样可以实现,但是效率不会太高。因此,还有一种更简便的方法来实现动态修改:

adapter.notifyDataSetChanged();

修改上述Activity的button点击事件:

button.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                data[n] = "修改的第" + n + "项";                adapter.notifyDataSetChanged();                listView.setSelection(n);                n++;            }        });

运行一下就可以看到,每次点击按钮实现修改item,并定位到修改的item。

遍历item

最常用的方法就是:

for(int i=0;i<listView.getChildCount();i++){ View view = listView.getChildAt(i); }

处理空内容的ListView

当ListView的内容为空时,看不会显示任何内容,其实如果显示一些文字告诉用户“没有任何信息”显得会获得更好地用户体验。而我们也有方法——setEmptyView()可以实现这一功能:
修改xml布局文件:

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.app.test.MainActivity">    <ListView  android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="match_parent" />    <TextView  android:id="@+id/t" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="抱歉!没有任何内容可以显示!" android:textSize="40dp" /></FrameLayout>

其次修改Activity的onCreate方法:

@Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //注掉,相当于msgList是空//        for (int i = 0; i < 20; i++) {//            msgList.add(new Msg(R.drawable.a, "第" + i + "项", "内容:吧啦吧啦~"));//        }        MyAdapter adapter = new MyAdapter(this, msgList);        listView = (ListView) findViewById(R.id.listView);        listView.setEmptyView(findViewById(R.id.t));        listView.setAdapter(adapter);    }

当ListView传入内容为空时,则显示TextView,有内容时不显示:

ListView滑动监听

ListView的滑动监听可以使用onTouchListener方法:

listView.setOnTouchListener(new View.OnTouchListener() {            @Override            public boolean onTouch(View v, MotionEvent event) {                switch (event.getAction()) {                    case MotionEvent.ACTION_DOWN:                        //手指按下                        break;                    case MotionEvent.ACTION_MOVE:                        //手指移动                        break;                    case MotionEvent.ACTION_UP:                        //手指抬起                        break;                }                return false;            }        });

通过手指的动作来绑定相应的事件,此方法是很多View共同的。

另一种是onScrollListener,通过set方法设置:

listView.setOnScrollListener()

并可以在匿名内部类OnScrollListener中重写OnScrollStateChanged()和OnScroll()方法:

listView.setOnScrollListener(new AbsListView.OnScrollListener() {        //当滑动状态改变时调用            @Override            public void onScrollStateChanged(AbsListView view, int scrollState) {                switch (scrollState) {                    case SCROLL_STATE_IDLE:                        Log.d("测试", "停止滑动");                        break;                    case SCROLL_STATE_TOUCH_SCROLL:                        Log.d("测试", "正在滑动");                        break;                    case SCROLL_STATE_FLING:                        Log.d("测试", "手指抛动后的惯性滑动");                        break;                }            }        //滑动时不断调用            @Override            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {                if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) {                    Log.d("测试", "滑动到底部");                } else if (firstVisibleItem == 0) {                    Log.d("测试", "滑动到顶部");                }                int lastVisibleItem = 0;                if (firstVisibleItem < lastVisibleItem) {                    Log.d("测试", "下滑");                } else if (firstVisibleItem > lastVisibleItem) {                    Log.d("测试", "上滑");                }                lastVisibleItem = firstVisibleItem;            }        });

可以复制上面的代码,运行感受下滑动的几种状态。

更多相关文章

  1. android DrawerLayout 实现侧滑菜单 知识整理(二)
  2. Android(安卓)context 文件模式
  3. 【Android】主线程调用Http请求无效
  4. Android(安卓)屏幕适配
  5. 利用Android两行代码真正杀死你的App
  6. Android批量图片加载经典系列——Volley框架实现多布局的新闻列
  7. 变更到Android4.4的问题
  8. android startActivityForResult的用法
  9. Android中隐式Intent的匹配规则

随机推荐

  1. Android App开发基础篇—四大组件之Activ
  2. android:绘图
  3. Android(安卓)NestedScrolling嵌套滚动的
  4. Android快速入门(一):Android介绍
  5. Android 源码解析-AsyncTask
  6. 初始Android
  7. Android网络收音机项目
  8. Android学习路线(八)为Action bar添加actio
  9. Android逆向之旅---解析编译之后的Androi
  10. 配置Android应用开发环境为什么需要安装