美文网首页安卓进阶
Android adapter ListView

Android adapter ListView

作者: 合肥黑 | 来源:发表于2016-07-23 23:30 被阅读556次
    一、适配器模式

    参考
    Android中Adapter的学习与思考
    Android源码之ListView的适配器模式

    我们知道Adapter就是适配器的意思。在GOF设计模式中存在一种设计模式,即是适配器模式(Adapter)。对设计模式的学习使我们知道:适配器模式能够将一个接口转换为客户所期望的另一个接口,使得原来由与接口不兼容而不能一切工作的类可以一起工作。举个简单例子:大家都知道笔记本的电源插头一般是三孔的,假定你家里没有三孔的插座,而只有两孔的怎么办。解决方法很简单,就是去买一个带三孔和两孔的插板,并且插板的插头应该是两孔的。这样问题就解决了嘛。这种解决的方法就是一种适配器模式,而插板就是适配器。

    作为最重要的View,ListView需要能够显示各式各样的视图,每个人需要的显示效果各不相同,显示的数据类型、数量等也千变万化。那么如何隔离这种变化尤为重要。Android的做法是增加一个Adapter层来应对变化,将ListView需要的接口抽象到Adapter对象中,这样只要用户实现了Adapter的接口,ListView就可以按照用户设定的显示效果、数量、数据来显示特定的Item View。
    通过代理数据集来告知ListView数据的个数( getCount函数 )以及每个数据的类型( getItem函数 ),最重要的是要解决Item View的输出。Item View千变万化,但终究它都是View类型,Adapter统一将Item View输出为View ( getView函数 ),这样就很好的应对了Item View的可变性。简单的说Adapter就是AdapterView视图与数据之间的桥梁,Adapter提供对数据项的访问,同时也负责为每一项数据产生一个View.

    Paste_Image.png Paste_Image.png

    由上述适配图就可以看出其实Android中的Adapter与设计模式中的Adapter特点都是一样的,虽然ListView需要的数据接口与Data Source并不兼容,但是通过Adater却可以让ListView使用Data Source。这与java中适配器模式的理念不谋而合!

    二、ArrayAdapter

    Android ArrayAdapter 详解
    使用详解及源码解析Android中的Adapter、BaseAdapter、ArrayAdapter、SimpleAdapter和SimpleCursorAdapter
    1.简单显示文本

    //activity_main.xml:
    <LinearLayout...>
       <ListView
           android:id="@+id/list_view"
           android:layout_width="match_parent"
           android:layout_height="match_parent">
       </ListView>
    </LinearLayout>
    
    //MainActivity.java
    public class MainActivity extends Activity{
        private String[] data = {"Apple","Banana","Orange"};
        
        protected void onCreate(Bundle savedInstanceState){
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
           
           //ArrayAdapter可以通过泛型来指定适配的数据类型,本例数据为String
           //simple_list_item_1是android内置布局文件,里面只有一个textview
           ArrayAdapter<String> adapter = new ArrayAdapter<String>(
           MainActivity.this,android.R.layout.simple_list_item_1,data);
           ListView listview = (ListView) findViewById(R.id.list_view);
           listview.setAdapter(adapter);
        }
    }
    

    2.简单显示文本,另外同时显示对应图片

    //新建一个实体类Fruit
    public class Fruit{
       private String name;
       private int imageId;
       
       public Fruit(String name, int imageId){
          this.name = name;
          this.imageId = imageId;
       }
       
       public String getName(){
           return name;
       }
       
       public int getImageId(){
           return imageId;
       }
    }
    
    //layout目录下新建fruit_item.xml
    <LinearLayout...>
       <ImageView fruit_image>
       <TextView fruit_name>
    </LinearLayout>
    
    
    //自定义适配器
    public class FruitAdapter extends ArrayAdapter<Fruit>{
       private int resourceId;
       //构造方法中指定了泛型Fruit
       public FruitAdapter(Context context,
       int textViewResourceId, List<Fruit> objects){
          super(context, textViewResourceId, objects);
          resourceId = textViewResourceId;
       }
    
       //getView方法在每个子项被滚动到屏幕内的时候调用
       @override
       public View getView(int postion, View convertView, ViewGroup parent){
          Fruit fruit = getItem(postion);
          //加载布局
          View view = LayoutInflater.from(getContext()).inflate(resourceId,null);
          ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
          TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
          fruitImage.setImageResource(fruit.getImageId());
          fruitName.setText(fruit.getName());
          //返回布局
          return view;
       }
    }
    
    //MainActivity.java
    
    private List<Fruit> fruitList = new ArrayList<Fruit>();
    private void initFruits(){
       Fruit apple = new Fruit("Apple", R.drawable.apple_pic);
       fruitList.add(apple);
       ...
    }
    
    protected void onCreate(){
       ...
       FruitAdapter adapter = new FruitAdapter
       (MainActivity.this,R.layout.fruit_item,fruitList);
       ...
       listview.setAdapter(adapter);
    }
    

    3.优化getView
    getView每次都把布局重新加载了一遍,观察一下getView(int postion, View convertView, ViewGroup parent),contentView参数用于将之前加载好的布局进行缓存,以便之后可以重用。所以可以这样:

    View view;
    if(converView == null){
       view = LayoutInflater.from(getContext()).inflate(resourceId,null);
    }else{
       view = convertView;//直接对convertView进行重用
    }
    

    不过我们可以继续优化,把那些findViewById也给缓存起来

    View view;
    ViewHolder viewHolder;
    if(converView == null){
       view = LayoutInflater.from(getContext()).inflate(resourceId,null);
       viewHolder = new ViewHolder();
       viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
       viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name);
       view.setTag(viewHolder);//缓存起来
    }else{
       view = convertView;//直接对convertView进行重用
       viewHolder = (ViewHolder) view.getTag();
    }
    viewHolder.fruitImage.setImageResource(fruit.getImageId());
    viewHolder.fruitName.setText(fruit.getName());
    return view;
    
    class ViewHolder{
       ImageView fruitImage;
       TextView fruitName;
    }
    

    4.点击事件

    listView.setOnItemClickListener(new OnItemClickListener(){
       @override
       public void onItemClick(AdapterView<?> parent,
       View view,int position, longid){
          Fruit fruit = fruitList.get(position);
          Toast.makeText(MainActivity.this,fruit.getName(),
          Toast.LENGTH_SHORT).show();
       }
    });
    
    三、SimpleAdaper

    参考
    使用详解及源码解析Android中的Adapter、BaseAdapter、ArrayAdapter、SimpleAdapter和SimpleCursorAdapter

    SimpleAdaper的作用是方便地将数据与XML文件定义的各种View绑定起来,从而创建复杂的UI。SimpleAdapter有很强的扩展性,可以自定义出各种效果。
    <pre>
    package com.ispring.adapter;

    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.ListView;
    import android.widget.SimpleAdapter;

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;

    public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ListView listView = (ListView)findViewById(R.id.listView);
    final String[] names = {"Windows","Mac OS","Linux","Android","Chrome OS"};
    final String[] descriptions = {
    "Windows是微软公司的操作系统",
    "Mac OS是苹果公司的操作系统",
    "Linux是开源免费操作系统",
    "Android是Google公司的智能手机操作系统",
    "Chrome OS是Google公司的Web操作系统"
    };
    final int[] icons = {
    R.drawable.windows,
    R.drawable.mac,
    R.drawable.linux,
    R.drawable.android,
    R.drawable.chrome
    };
    List<Map<String, Object>> list = new ArrayList<>();
    for(int i = 0; i < names.length; i++){
    HashMap<String, Object> map = new HashMap<>();
    map.put("name", names[i]);
    map.put("description", descriptions[i]);
    map.put("icon", icons[i]);
    list.add(map);
    }
    //每个数据项对应一个Map,from表示的是Map中key的数组
    String[] from = {"name", "description", "icon"};
    //数据项Map中的每个key都在layout中有对应的View,
    //to表示数据项对应的View的ID数组
    int[] to = {R.id.name, R.id.description, R.id.icon};
    //R.layout.item表示数据项UI所对应的layout文件
    SimpleAdapter adapter = new SimpleAdapter(this, list, R.layout.item, from, to);
    listView.setAdapter(adapter);
    }
    }
    </pre>

    其中R.layout.item表示数据项UI所对应的layout文件,如下所示:
    <pre>
    <?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="wrap_content"
    android:orientation="horizontal"
    android:paddingTop="5dp"
    android:paddingBottom="5dp">
    <ImageView android:id="@+id/icon"
    android:layout_width="100dp"
    android:layout_height="wrap_content" />
    <LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:paddingLeft="10dp">
    <TextView android:id="@+id/name"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="@dimen/defaultFontSize" />
    <TextView android:id="@+id/description"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="@dimen/defaultFontSize"
    android:layout_marginTop="10dp"/>
    </LinearLayout>
    </LinearLayout>
    </pre>

    SimpleAdapter只有一个构造函数,签名如下所示:
    public SimpleAdapter (Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)

    • data表示的是List数据源,其中List中的元素都是Map类型,并且Map的key是String类型,Map的value可以是任意类型,我们一般使用HashMap<String, Object>作为List中的数据项。
    • resource表示数据项UI所对应的layout文件,在本例中即R.layout.item。在本例中,每条数据项都要包含图片、名称、描述三条信息,所以我们在item.xml中定义了一个ImageView表示图片,两个TextView分别表示名称和描述,并且都设置了ID值。
    • 每个数据项对应一个Map,from表示的是Map中key的数组。
    • 数据项Map中的每个key都在layout中有对应的View,to表示数据项对应的View的ID数组。
    四、SimpleCursorAdapter

    SimpleCursorAdapter可以从数据库中读取数据显示在列表上。

    五、自定义baseAdapter

    参考Android中万能的BaseAdapter的使用来一个未优化版本:
    <pre>
    package com.tutor.baseadapter;
    import android.app.Activity;
    import android.graphics.Color;
    import android.os.Bundle;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.GridView;
    import android.widget.ListView;
    import android.widget.Spinner;
    import android.widget.TextView;
    public class BaseAdapterDemo extends Activity {

    private Spinner mSpinner;  
    private ListView mListView;  
    private GridView mGridView;  
    private MyAdapter mMyAdapter;  
    @Override 
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        setupViews();  
    }  
       
    public void setupViews(){  
        mMyAdapter = new MyAdapter();  
        mSpinner = (Spinner)findViewById(R.id.spinner);  
        mSpinner.setAdapter(mMyAdapter);  
        mListView = (ListView)findViewById(R.id.listview);  
        mListView.setAdapter(mMyAdapter);  
        mGridView = (GridView)findViewById(R.id.gridview);  
        mGridView.setAdapter(mMyAdapter);  
        mGridView.setNumColumns(2);  
       
    }  
       
    //定义自己的适配器,注意getCount和getView方法   
    private class MyAdapter extends BaseAdapter{  
        @Override 
        public int getCount() {  
            // 这里我就返回10了,也就是一共有10项数据项   
            return 10;  
        }  
        @Override 
        public Object getItem(int arg0) {  
            return arg0;  
        }  
        @Override 
        public long getItemId(int position) {  
            return position;  
        }  
        @Override 
        public View getView(int position, View convertView, ViewGroup parent) {  
            // position就是位置从0开始,convertView是Spinner,ListView中每一项要显示的view   
            //通常return 的view也就是convertView   
            //parent就是父窗体了,也就是Spinner,ListView,GridView了.   
            //TextView mTextView = new TextView(getApplicationContext());  
            //mTextView.setText("BaseAdapterDemo");  
            //mTextView.setTextColor(Color.RED);
            convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.baseadapter_provider,null);
            TextView mTextView = (TextView)convertView.findViewById(R.id.textview);
            mTextView.setText("BaseAdapterDemo" + position);
            mTextView.setTextColor(Color.RED);
            return mTextView;  
        }  
           
    }  
    

    }
    </pre>

    优化思路:通过convertView+ViewHolder来实现,使用ViewHolder这个静态类的好处是缓存了显示数据的View,加快了UI的响应速度。当我们判断convertView == null,若为空,就会根据设计好的布局文件布局,并未convertView赋值,并且生成一个viewHolder来绑定convertView的各个View控件。再用convertView的setTag将viewHolder设置到Tag中,以便系统第二次绘制ListView时从Tag中取出。
    参考
    Android--Adapter深入理解及ListView优化
    android代码优化----ListView中自定义adapter的封装

    package com.example.listview_baseadapter;  
      
    import java.util.ArrayList;  
    import java.util.HashMap;  
    import java.util.List;  
    import java.util.Map;  
      
    import android.app.Activity;  
    import android.content.Context;  
    import android.os.Bundle;  
    import android.view.LayoutInflater;  
    import android.view.Menu;  
    import android.view.View;  
    import android.view.ViewGroup;  
    import android.widget.BaseAdapter;  
    import android.widget.ImageView;  
    import android.widget.ListView;  
    import android.widget.TextView;  
      
    public class MainActivity extends Activity {  
      
        private ListView listView = null;  
        private List<Map<String, Object>> data = null;  
      
        @Override  
        protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.activity_main);  
            listView = (ListView) findViewById(R.id.list);// 取得控件  
            data = getData();//获取数据  
            MyAdapter adapter = new MyAdapter(this);  
            listView.setAdapter(adapter);  
        }  
      
        @Override  
        public boolean onCreateOptionsMenu(Menu menu) {  
            // Inflate the menu; this adds items to the action bar if it is present.  
            getMenuInflater().inflate(R.menu.main, menu);  
            return true;  
        }  
      
        // 得到数据  
        public List<Map<String, Object>> getData() {  
            List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();  
      
             Map<String, Object> map;  
                for(int i=0;i<10;i++)  
                {  
                    map = new HashMap<String, Object>();  
                    map.put("img", R.drawable.bgs);  
                    map.put("title", "Coder");  
                map.put("content", "简单Coding,快乐生活~~~~");  
                    list.add(map);  
                }  
                return list;  
        }  
      
        // ViewHolder静态类  
        static class ViewHolder {  
            public ImageView img;  
            public TextView title;  
            public TextView content;  
        }  
      
        public class MyAdapter extends BaseAdapter {  
      
            private LayoutInflater mInflater = null;  
      
            private MyAdapter(Context context) {  
                // 根据context上下文加载布局  
                this.mInflater = LayoutInflater.from(context);  
            }  
      
            @Override  
            public int getCount() {  
                // 在此适配器中所代表的数据集中的条目数  
                return data.size();  
            }  
      
            @Override  
            public Object getItem(int position) {  
                  
                // 获取数据集中与指定索引对应的数据项  
                return position;  
            }  
      
            @Override  
            public long getItemId(int position) {  
              
                // 获取在列表中与指定索引对应的行id  
                return position;  
            }  
      
            // 获取一个在数据集中指定索引的视图来显示数据  
            @Override  
            public View getView(int position, View convertView, ViewGroup parent) {
                ViewHolder holder = null;  
                // 如果缓存convertView为空,则需要创建View  
                if (convertView == null) {  
                    holder = new ViewHolder();  
                    // 根据自定义的Item布局加载布局  
                    convertView = mInflater.inflate(R.layout.list_item, null);  
                    holder.img = (ImageView) convertView.findViewById(R.id.img);  
                    holder.title = (TextView) convertView.findViewById(R.id.title);  
                    holder.content = (TextView) convertView.findViewById(R.id.content);  
                    // 将设置好的布局保存到缓存中,并将其设置在Tag里,以便后面方便取出Tag  
                    convertView.setTag(holder);  
                } else {  
                    holder = (ViewHolder) convertView.getTag();  
                }  
                holder.img.setBackgroundResource((Integer) data.get(position).get("img"));  
                holder.title.setText((String) data.get(position).get("title"));
                holder.content.setText((String)data.get(position).get("content"));  
      
                return convertView;  
            }  
      
        }  
      
    }
    

    注意这段代码:

        // ViewHolder静态类  
        static class ViewHolder {  
            public ImageView img;  
            public TextView title;  
            public TextView content;  
        } 
    

    参考android listview 声明ViewHolder内部类时,为什么建议使用static关键字
    这个问题也是我每次面试别人必问的问题之一。其实这个是考静态内部类和非静态内部类的主要区别之一。非静态内部类会隐式持有外部类的引用,就像大家经常将自定义的adapter在Activity类里,然后在adapter类里面是可以随意调用外部activity的方法的。当你将内部类定义为static时,你就调用不了外部类的实例方法了,因为这时候静态内部类是不持有外部类的引用的。声明ViewHolder静态内部类,可以将ViewHolder和外部类解引用。大家会说一般ViewHolder都很简单,不定义为static也没事吧。确实如此,但是如果你将它定义为static的,说明你懂这些含义。万一有一天你在这个ViewHolder加入一些复杂逻辑,做了一些耗时工作,那么如果ViewHolder是非静态内部类的话,就很容易出现内存泄露。如果是静态的话,你就不能直接引用外部类,迫使你关注如何避免相互引用。 所以将 ViewHolder内部类 定义为静态的,是一种好习惯.

    另外,使用使用静态内部类实现单例模式,这种方法也是《Effective Java》上所推荐的

    public class Singleton {
        private static class SingletonHolder {
            private static final Singleton INSTANCE = new Singleton();
        }
        private Singleton (){}
        public static final Singleton getInstance() {
            return SingletonHolder.INSTANCE;
        }
    }
    

    这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。

    六、使用技巧

    参考《安卓群英传》P68
    1.设置项目分隔线

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

    2.隐藏滚动条
    android:scrollbars="none"
    3.取消点击效果,可以使用安卓自带透明色
    android:listSelector="@android:color/transparent"
    4.设置显示在第几项
    默认显示第一项,可以使用listView.setSelection(N)设置显示第N项。
    当然这个方法类似scrollTo,瞬间完成移动。缓动可以使用:

    mListView.smoothScrollBy(distance,duration);
    mListView.smoothScrollbyOffset(offset);
    mListView.smoothScrollToPosition(index);
    

    5.动态修改

    mData.add("new");
    mAdapter.notifyDataSetChanged();
    mListView.setSelection(mData.size() - 1);
    

    6.遍历

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

    7.设置空数据时如何显示

    listView.setEmptyView(findViewById(R.id.empty_view));
    

    8.点击

    listView.setOnItemClickListener(new OnItemClickListener(){
       @Override
       public void onItemClick(AdapterView<?> parent, View view, int position, long id){
          Fruit fruit = fruitList.get(position);//通过position判断用户点击的是哪一个子项
          Toast.makeText...
       }
    );
    

    9.onTouchListener
    10.onScrollListener

    //通过不同状态来设置一些flag,来区分不同的滑动状态,供其他方法处理
    mListView.setOnScrollListener(new OnScrollListener(){
       public void onScrollStateChanged(AbsListView view, int scrollState){
          switch(scrollState){
             case OnScrollListener.SCROLL_STATE_IDLE:
             //滑动停止时
             break;
             case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
             //正在滚动
             break;
             case OnScrollListener.SCROLL_STATE_FLING:
             //手指抛动时 离开listview后由于惯性继续滑动
             break;
          }
       }
    });
    
    //onScroll在listview滚动时会一直回调
    public void onScroll(AbsListView view,int firstVisibleItem,int visibleItemCount,int totalItemCount){
       //firstVisibleItem 当前能看见的第一个Item的ID,从0开始
       //visibleItemCount 当前能看见的Item总数
       //totalItemCount 整个ListView的Item总数(包括没有显示完整的Item)
       
       if(firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0){
          //滚动到最后一行
       }
       
       if(firstVisibleItem > lastVisibleItemPosition){
          //上滑
       }else if(firstVisibleItem < lastVisibleItemPosition){
          //下滑
       }
       lastVisibleItemPosition = firstVisibleItem;
       
       //获取可视区域内最后一个Item的id
       mListView.getLastVisiblePosition()
       //获取可视区域内第一个Item的id
       mListView.getFirstVisiblePosition()
    }
    

    11.优化卡顿-参考《安卓开发艺术探索》
    首先,不要在getView中执行耗时操作。比如加载图片,必须要用异步方式处理。
    其次,控制异步操作的执行频率。如果用户刻意频繁上下滑动,会在一瞬间产生上百个异步任务,这会造成线程池拥堵并随即带来大量的UI更新,这是没有意义的。可以在列表滑动时停止加载图片,静止时再加载。

    public void onScrollStateChanged(AbsListView view,int scrollState){
       if(scrollState == OnScrollListener.SCROLL_STATE_IDLE){
          mIsGridViewIdle = true;
          mImageAdapter.notifyDataSetChanged();
       }else{
          mIsGridViewIdle = false;
       }
    }
    
    //在getView方法中,仅当列表静止时才加载图片:
    if(mIsGridViewIdle && mCanGetBitmapFromNetWrok){
       imageView.setTage(uri);
       mImageLoader.bindBitmap(uri,imageView,mImageWidth,mImageWidth);
    }
    

    另外,可以开启硬件加速来解决莫名的卡顿问题。通过设置android:hardwareAccelerated = "true"即可为Activity开启硬件加速。

    七、常用扩展

    1.使用maxOverScrollY属性,让列表滚动到底端或顶端后,继续向下或向上滑动一段距离。

    private void initView() {
            //让不同分辨率弹性滑动距离基本一致
            DisplayMetrics metrics = mContext.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,
                    maxOverScrollX, mMaxOverDistance,
                    isTouchEvent);
        }
    

    2.向下滑动时,标题栏和悬浮按钮自动消失,让用户有更大空间阅读。

    package com.imooc.myapplication;
    
    import android.animation.ObjectAnimator;
    import android.app.Activity;
    import android.os.Bundle;
    import android.support.v7.widget.Toolbar;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewConfiguration;
    import android.widget.AbsListView;
    import android.widget.ArrayAdapter;
    import android.widget.ListView;
    
    
    public class ScrollHideListView extends Activity {
    
        private Toolbar mToolbar;
        private ListView mListView;
        private String[] mStr = new String[20];
        private int mTouchSlop;
        private float mFirstY;
        private float mCurrentY;
        private int direction;
        private ObjectAnimator mAnimator;
        private boolean mShow = true;
    
        View.OnTouchListener myTouchListener = new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        mFirstY = event.getY();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        mCurrentY = event.getY();
                        if (mCurrentY - mFirstY > mTouchSlop) {
                            direction = 0;// down
                        } else if (mFirstY - mCurrentY > mTouchSlop) {
                            direction = 1;// up
                        }
                        if (direction == 1) {
                            if (mShow) {
                                toolbarAnim(1);//show
                                mShow = !mShow;
                            }
                        } else if (direction == 0) {
                            if (!mShow) {
                                toolbarAnim(0);//hide
                                mShow = !mShow;
                            }
                        }
                        break;
                    case MotionEvent.ACTION_UP:
                        break;
                }
                return false;
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.scroll_hide);
            //系统认为最低滑动距离,超过此值就是滑动状态
            mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
            mToolbar = (Toolbar) findViewById(R.id.toolbar);
            mListView = (ListView) findViewById(R.id.listview);
            for (int i = 0; i < mStr.length; i++) {
                mStr[i] = "Item " + i;
            }
            View header = new View(this);
            header.setLayoutParams(new AbsListView.LayoutParams(
                    AbsListView.LayoutParams.MATCH_PARENT,
                    (int) getResources().getDimension(
                            R.dimen.abc_action_bar_default_height_material)));
            mListView.addHeaderView(header);
            mListView.setAdapter(new ArrayAdapter<String>(
                    ScrollHideListView.this,
                    android.R.layout.simple_expandable_list_item_1,
                    mStr));
            mListView.setOnTouchListener(myTouchListener);
        }
    
        private void toolbarAnim(int flag) {
            //位移属性动画
            if (mAnimator != null && mAnimator.isRunning()) {
                mAnimator.cancel();
            }
            if (flag == 0) {
                mAnimator = ObjectAnimator.ofFloat(mToolbar,
                        "translationY", mToolbar.getTranslationY(), 0);
            } else {
                mAnimator = ObjectAnimator.ofFloat(mToolbar,
                        "translationY", mToolbar.getTranslationY(),
                        -mToolbar.getHeight());
            }
            mAnimator.start();
        }
    }
    

    3.像微信聊天,区分收到消息和发送消息的两种布局

    package com.imooc.myapplication;
    
    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;
    
    public class ChatItemListViewAdapter extends BaseAdapter {
    
        private List<ChatItemListViewBean> mData;
        private LayoutInflater mInflater;
    
        public ChatItemListViewAdapter(Context context,
                                       List<ChatItemListViewBean> data) {
            this.mData = data;
            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 int getItemViewType(int position) {
            //用来返回第position个Item是何类型
            ChatItemListViewBean bean = mData.get(position);
            return bean.getType();
        }
    
        @Override
        public int getViewTypeCount() {
            //用来返回不同布局的总数
            return 2;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder;
            if (convertView == null) {
                //根据类型生成不同的布局
                if (getItemViewType(position) == 0) {
                    holder = new ViewHolder();
                    convertView = mInflater.inflate(
                            R.layout.chat_item_itemin, null);
                    holder.icon = (ImageView) convertView.findViewById(
                            R.id.icon_in);
                    holder.text = (TextView) convertView.findViewById(
                            R.id.text_in);
                } else {
                    holder = new ViewHolder();
                    convertView = mInflater.inflate(
                            R.layout.chat_item_itemout, null);
                    holder.icon = (ImageView) convertView.findViewById(
                            R.id.icon_out);
                    holder.text = (TextView) convertView.findViewById(
                            R.id.text_out);
                }
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
            holder.icon.setImageBitmap(mData.get(position).getIcon());
            holder.text.setText(mData.get(position).getText());
            return convertView;
        }
    
        public final class ViewHolder {
            public ImageView icon;
            public TextView text;
        }
    }
    
    

    相关文章

      网友评论

      本文标题:Android adapter ListView

      本文链接:https://www.haomeiwen.com/subject/vopwjttx.html