首页 技术 正文
技术 2022年11月18日
0 收藏 574 点赞 5,117 浏览 13206 个字

Android群英传笔记——第四章:ListView使用技巧


最近也是比较迷茫,但是有一点点还是要坚持的,就是学习了,最近离职了,今天也是继续温习第四章ListView,也拖了其实也挺久的了,listview可谓是老牌大将了,很多的应用场景都要使用它,他也是我们用得最多的控件之一了,虽然现在出来了一个RecyclerView,但是ListView的地位一时半会儿还是撼动不了的,这就促使我们更加应该去把他掌握了

一.Listview常用优化技巧

我们一步步来把ListView学习好

1.使用ViewHolder模式提高效率

ViewHolder模式是提高ListView效率的一个很重要的方法,他充分利用了ListView的视图缓存机制,避免每次调用getView()的时候去通过findViewById()实例化控件,有人测试结果效率要高百分之五十,我们只需要在自定义的Adapter里面定义一个内部类ViewHolder即可,代码如下:

    public final class ViewHolder{        public ImageView img;        public TextView tv;    }

然后我们在getView的时候复用就行了,我们来看一下完整的Adapter

package com.lgl.listviewdemo;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import java.util.List;/** * 自定义Adapter * Created by LGL on 2016/3/10. */public class MyAdapter extends BaseAdapter {    private List<String> mData;    private LayoutInflater mInflater;    //构造方法    public MyAdapter(Context context, List<String> mData) {        this.mData = mData;        mInflater = LayoutInflater.from(context);    }    //返回长度    @Override    public int getCount() {        return mData.size();    }    @Override    public Object getItem(int position) {        return mData.get(position);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        ViewHolder viewHolder = null;        //判断是否有缓存        if (convertView == null) {            viewHolder = new ViewHolder();            //通过LayoutInflater去实例化布局            convertView = mInflater.inflate(R.layout.item, null);            viewHolder.img = (ImageView) convertView.findViewById(R.id.img);            viewHolder.tv = (TextView) convertView.findViewById(R.id.tv);            convertView.setTag(viewHolder);        } else {            //通过TAG找到缓存的布局            viewHolder = (ViewHolder) convertView.getTag();        }        //设置布局中要显示的东西‘        viewHolder.tv.setText(mData.get(position));        return convertView;    }    public final class ViewHolder {        public ImageView img;        public TextView tv;    }}

如果我们要使用的话,我们就可以直接使用adapter了

package com.lgl.listviewdemo;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.widget.ListView;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity {    private ListView listview;    private MyAdapter myAdapter;    private List<String> list = new ArrayList<String>();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        listview = (ListView) findViewById(R.id.listview);        for (int i = 0; i < 20; i++) {            list.add("第" + i + "张");        }        myAdapter = new MyAdapter(this, list);        listview.setAdapter(myAdapter);    }}

我们运行一下

OK我们继续

2.设置项目间分割栏、

ListView在各个项目之中,可以通过分割线进行区分的,系统也提供了两个属性来设置item间的颜色和高度

android:dividerHeight="10dp"android:divider="@android:color/holo_blue_bright"

我们来运行一下

但是如果你是需要去掉这个分割线的话,其实也好办

 android:divider="@null"

看图

3.隐藏listview的滚动条

这个看需求吧,虽然说实话,我是很不喜欢这个滚动条的,但是不可否认他还是需要用来显示进度的,去掉的方法也很简单,加一条属性就可以了

 android:scrollbars="none"

4.取消ListView的item点击效果

先来看一下点击的效果,是有一点点灰色的点击效果,我们把他给去掉,加一个点击反馈颜色透明的属性就可以了

 android:listSelector="@android:color/transparent"

这里;你也可以直接填#00000000

5.设置ListView需要显示在第几项

ListView默认是从上到下的,我们可以认为的去设置他要显示的item

listview.setSelection(15);

这个就有点类似于平滑的效果了

listview.smoothScrollBy(1,15);listview.smoothScrollByOffset(15);listview.smoothScrollToPosition(15);

6.动态修改ListView

这个就很常用了,我们在某一个场景的时候需要对listview增加数据,我们应该怎么样做尼?我们在xml里新增加一个button

<?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">    <ListView        android:id="@+id/listview"        android:layout_width="match_parent"        android:layout_height="0dp"        android:layout_weight="1" />    <Button        android:id="@+id/add_data"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="添加数据" /></LinearLayout>

然后我们的点击事件就可以这样写了

 @Override    public void onClick(View view) {        switch (view.getId()) {            case R.id.add_data:                list.add("我是新增加的数据");                //通知刷新                myAdapter.notifyDataSetChanged();                break;        }    }

来看看效果吧

7.遍历所有的item

ListView作为一个ViewGroup,他提供了很多操纵子View的方法,最常用的就是getChilaAt()来获取View了

for (int j = 0; j < listview.getChildCount();j++){            View v = listview.getChildAt(12);        }

8.处理空ListView

有时候我们获取一条数据的时候,他需要显示列表,有的可能是空,这样的话,我们就得处理一下了,我们在布局中新加一个textview

 <TextView        android:id="@+id/tv_null"        android:text="没有数据"        android:layout_width="match_parent"        android:layout_height="wrap_content" />

只要在代码中设置如果是空数据的话就加载这个布局就可以

 listview.setEmptyView(findViewById(R.id.tv_null));

这样的话,如果ListView为空数据的时候就会设置这句话了

9.ListView滑动监听

ListView的滑动监听可是ListView的很重要的一个技巧,很多应用场景需要重写ListView的其实都要在滑动监听上下很大的功夫,通过判断滑动事件来处理不同的逻辑这是很有必要的,开发者通常还需要GestureDetector手势识别,VelocityTracker滑动速度检测来辅助监听,这里介绍两种监听方法,一种是OnTouchListener,另一中是OnScrollListener

-1. OnTouchListener

OnTouchListener是View的监听事件,包括ACTION_DOWN,UP,MOVE等,通过坐标的改变老发生不同的逻辑

listview.setOnTouchListener(new View.OnTouchListener() {            @Override            public boolean onTouch(View view, MotionEvent motionEvent) {                switch (motionEvent.getAction()) {                    case MotionEvent.ACTION_DOWN:                        //触摸时操作                        break;                    case MotionEvent.ACTION_MOVE:                        //移动时操作                        break;                    case MotionEvent.ACTION_UP:                        //离开时操作                        break;                }                return true;            }        });

-2 OnScrollListener

OnScrollListener是AbsListView的监听事件,他封装了很多ListView的相关信息,所以用起来很灵活

 listview.setOnScrollListener(new AbsListView.OnScrollListener() {            @Override            public void onScrollStateChanged(AbsListView absListView, int i) {                               switch (i) {                    case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:                        //滚动停止                        break;                    case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:                        //正在滚动                        break;                    case AbsListView.OnScrollListener.SCROLL_STATE_FLING:                        //手指抛动时,即手指用力滑动的时候                        break;                }            }            @Override            public void onScroll(AbsListView absListView, int i, int i1, int i2) {                    //滚动的时候一直在调用            }        });

OnScrollListener中有两个回调方法onScrollStateChanged()和onScroll,我们先来看看onScrollStateChanged吧,这个方法的参数i来决定其回调的次数,有三种模式

  • OnScrollListener.SCROLL_STATE_IDLE://滚动停止
  • OnScrollListener.SCROLL_STATE_TOUCH_SCROLL://正在滚动
  • OnScrollListener.SCROLL_STATE_FLING://手指抛动时,即手指用力滑动的时候

当用户没有做手指抛动的动作时,这个方法只会调用2次,否则就调用三次,差别就在于手指抛动的这个状态,通常情况下,我们会在这个方法中通过不同的状态来标注一些FLAG,我们来看下onScrill()这个方法

                /**                 * firstVisibleItem:表示在现时屏幕第一个ListItem(部分显示的ListItem也算)在整个ListView的位置(下标从0开始)                 * visibleItemCount:表示在现时屏幕可以见到的ListItem(部分显示的ListItem也算)总数                 * totalItemCount:表示ListView的ListItem总数                 * listView.getFirstVisiblePosition()表示在现时屏幕第一个ListItem(第一个ListItem部分显示也算)在整个ListView的位置(下标从0开始)                 * listView.getLastVisiblePosition()表示在现时屏幕最后一个ListItem(最后ListItem要完全显示出来才算)在整个ListView的位置(下标从0开始)                 */

这里有一点要注意的是,当前能看到的item数也包括没有显示完全的,这样我们就又能Get到一些技巧了

 if(i+i1 == i2 &&i2>0){     //滚动到最后一行     }

再比如监听滑动放心

 int i3 = i; if(i>i3){     //上滑  }else if(i<i3){     //下滑  }

通过一个成员变量来记住上一个可视item就知道滑动的方法了,当然,ListView还提供了很多获取位置信息的方法

 //获取可视区域内最后一个item的idlistview.getLastVisiblePosition(); //获取可视区域内第一个item的id listview.getFirstVisiblePosition();

二.ListView常用扩展

虽然ListView应用很广泛,但是毕竟是一个显示的东西,扩展性肯定要的,我们接下来说几种常见的扩展

1.具有弹性的ListView

Android默认滑动到顶部或者底部只会有一个阴影,而在5.X之后改变成了半圆的阴影

而在IOS上,列表是具有弹性的,即滚动到顶部或者底部,会再滚动一段距离,这样的设计感觉还是挺友好的,我们也来模仿一下

我们在查看ListView的源码的时候会发现一个控制滑动到边缘的处理方法

@Override    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);    }

我們可以看到这样一个参数maxOverScrollY,就是他负责控制滑动的个数的,默认是0,我们重写ListView

package com.lgl.listviewdemo;import android.content.Context;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.widget.ListView;/** * 弹性ListView * Created by lgl on 16/3/20. */public class ListViewScroll extends ListView {    private int mMaxOverdistance ;    public ListViewScroll(Context context, AttributeSet attrs) {        super(context, attrs);        //通过分辨率来调节滑动尺度        DisplayMetrics metrics = context.getResources().getDisplayMetrics();        float density = metrics.density;        mMaxOverdistance = (int)(density*mMaxOverdistance);    }    @Override    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, mMaxOverdistance, maxOverScrollY, isTouchEvent);    }}

2.自动显示,隐藏布局的ListView

相信看过Google最新的应用,或者使用了MD风格的应用都知道,列表滑动的时候actionbar可以根据状态显示或者隐藏,

这一段作者写的很乱

这要是讲一下大概,需要使用ToolsBar,然后一个listview

<android.support.v7.widget.Toolbar        android:id="@+id/toolbar"        android:layout_width="match_parent"        android:layout_height="?attr/actionBarSize"        android:background="?attr/colorPrimary" />

不详细记了,在我讲MD风格的时候会详细介绍的

3.聊天ListView

相信大家都看过聊天的ListView吧,这个主要是adapter做手脚,其实设计者早就想到了这种方法,所以在继承BaseAdapter的时候还需要重写两个方法

 @Override    public int getItemViewType(int position) {        return super.getItemViewType(position);    }    @Override    public int getViewTypeCount() {        return super.getViewTypeCount();    }

我们这里大致的了解一个思路,我们可以设置一个tag来识别不同的方向,先写两个item,分别是左边的和右边的布局,然后,我们再来写个实体类Bean

Bean

package com.lgl.listviewdemo;import android.graphics.Bitmap;/** * 实体类 * Created by lgl on 16/3/20. */public class Bean {    private int type;    private String text;    private Bitmap icon;    public Bean() {    }    public int getType() {        return type;    }    public void setType(int type) {        this.type = type;    }    public String getText() {        return text;    }    public void setText(String text) {        this.text = text;    }    public Bitmap getIcon() {        return icon;    }    public void setIcon(Bitmap icon) {        this.icon = icon;    }}

现在就可以编写我们的Adapter了

SpeakAdapter

package com.lgl.listviewdemo;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import java.util.List;/** * Created by lgl on 16/3/20. */public class SpeakAdapter extends BaseAdapter{    private List<Bean>mData;    private LayoutInflater mInflater;    //构造方法    public SpeakAdapter(Context context,List<Bean>mData){        this.mData = mData;        mInflater = LayoutInflater.from(context);    }    @Override    public int getCount() {        return mData.size();    }    @Override    public Object getItem(int position) {        return mData.get(position);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        ViewHolder viewHolder;        if(convertView == null){            //区分左右            if(getItemViewType(position) == 0){                viewHolder = new ViewHolder();                convertView = mInflater.inflate(R.layout.left_item,null);                viewHolder.icon = (ImageView) convertView.findViewById(R.id.left_icon);                viewHolder.text = (TextView) convertView.findViewById(R.id.tv_left);            }else{                viewHolder = new ViewHolder();                convertView = mInflater.inflate(R.layout.right_item,null);                viewHolder.icon = (ImageView) convertView.findViewById(R.id.right_icon);                viewHolder.text = (TextView) convertView.findViewById(R.id.tv_right);            }            convertView.setTag(viewHolder);        }else{            viewHolder = (ViewHolder) convertView.getTag();        }        viewHolder.icon.setImageBitmap(mData.get(position).getIcon());        viewHolder.text.setText(mData.get(position).getText());        return convertView;    }    @Override    public int getItemViewType(int position) {        Bean bean = mData.get(position);        return bean.getType();    }    @Override    public int getViewTypeCount() {        return 2;    }    public final class  ViewHolder{        public ImageView icon;        public TextView text;    }}

这里大致的思路也就是在getview中区分,现在我们的SpeakListViewActivity中就可以这样写

package com.lgl.listviewdemo;import android.graphics.BitmapFactory;import android.os.Bundle;import android.os.PersistableBundle;import android.support.v7.app.AppCompatActivity;import android.widget.ListView;import java.util.ArrayList;import java.util.List;/** * 聊天ListView * Created by lgl on 16/3/20. */public class SpeakListViewActivity extends AppCompatActivity {    private ListView mListview;    @Override    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {        super.onCreate(savedInstanceState, persistentState);        setContentView(R.layout.activity_speak);        mListview = (ListView) findViewById(R.id.listview);        Bean bean1 = new Bean();        bean1.setType(0);        bean1.setIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));        bean1.setText("我是左边");        Bean bean2 = new Bean();        bean2.setType(1);        bean2.setIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));        bean2.setText("我是右边");        List<Bean>data = new ArrayList<Bean>();        data.add(bean1);        data.add(bean2);        SpeakAdapter adapter = new SpeakAdapter(this,data);        mListview.setAdapter(adapter);    }}

把数据装载好了就行,这里只是演示

4.动态改变ListView的布局

都是什么鬼,写的不是很详细呀

通常情况下,如果要动态的改变点击item的布局来达到Focus的效果,一般有两种方法,一是将两个布局写在一起,通过布局的显示隐藏来达到切换布局的效果,另外一种则是在getView的时候,通过判断来选择不同的加载不同的布局,两种方法都有利弊,关键还是要看看应用场景,所以我们还是得在Adapter下手脚

 private View addFocusView(int i){        ImageView iv = new ImageView(mContext);        iv.setImageResource(R.mipmap.ic_launcher);        return iv;    }    private View addNormalView(int i){        LinearLayout layout = new LinearLayout(mContext);        layout.setOrientation(LinearLayout.HORIZONTAL);        ImageView iv = new ImageView(mContext);        iv.setImageResource(R.mipmap.ic_launcher);        layout.addView(iv,new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT));        TextView tv = new TextView(mContext);        tv.setText(list.get(i));        layout.addView(tv, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));        layout.setGravity(Gravity.CENTER);        return layout;    }

在这两个方法中就可以根据item的不同位置显示不同的信息了,下面我们回到adapter中

@Override    public View getView(int position, View convertView, ViewGroup parent) {        LinearLayout layout = new LinearLayout(mContext);        layout.setOrientation(LinearLayout.VERTICAL);        if(mCurrentItem == position){            layout.addView(addFocusView(position));        }else {            layout.addView(addNormalView(position));        }        return convertView;    } 

这样就可以自由选择了

那我们现在就来监听他的点击逻辑吧

 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {            @Override            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {                myAdapter.setCurrentItem(position);                myAdapter.notifyDataSetChanged();            }        });

OK,搞定,实在的说吧,这一章不止这点内容,不过本书很多都一笔带过了,当然,的确进阶书不需要写很详细,但是…额….我们接着看下一章吧!

笔记下载链接:http://pan.baidu.com/s/1c0U7k2W 密码:9v0g

Demo下载地址:http://download.csdn.net/detail/qq_26787115/9467353

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,964
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,486
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,331
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,114
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,747
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,781