zoukankan      html  css  js  c++  java
  • Android_适配器(adapter)之BaseAdapter

    BaseAdapter是应用最多的一种适配了。它是一个抽象类,需要重写方法完成自定义适配器的功能,这就比较自由灵活,能实现各种想要的效果。

    之前讲到的SimpleAdapterArrayAdapter 就是它的子类。

    下面介绍如何使用BaseAdapter实现自定义适配器。

    基础知识点

    使用BaseAdapter,只需继承它并实现下面4个方法即可:

    public int getCount() //item的数目,即适配器要显示的数据项个数
    public Object getItem(int position) //获取指定位置的数据项
    public long getItemId(int position) //获取指定位置的数据项id
    public View getView(int position, View convertView, ViewGroup parent) //获取每一项显示内容-view

    下面通过简单示例,详细讲解BaseAdapter的使用以及注意事项

    示例讲解

    这次示例使用GridView作为显示组件。

    布局文件,仅一个GridView的组件:base_adapter_act.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <GridView
            android:id="@+id/base_adapter_lv"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </RelativeLayout>

     

    文本资源,适配器填充的数据。和SimpleAdapter一样的:

    <array name="anime_name">
            <item>海贼王</item>
            <item>进击的巨人</item>
            <item>火影忍者</item>
            <item>斩赤红之瞳</item>
            <item>秦时明月</item>
            <item>西游记</item>
            <item>葫芦娃</item>
        </array>
    
        <array name="anime_author">
            <item>尾田荣一郎</item>
            <item>谏山创</item>
            <item>岸本齐史</item>
            <item>タカヒロ</item>
            <item>玄机科技</item>
            <item>央视</item>
            <item>上海美术电影</item>
        </array>

     

    每一项的布局文件:base_adapter_grid_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <ImageView android:id="@+id/anime_cover_img"
            android:layout_width="match_parent"
            android:layout_margin="1dp"
            android:layout_height="100dp"
            android:scaleType="fitXY"/>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="35dp"
            android:orientation="vertical"
            android:layout_below="@id/anime_cover_img">
            <TextView
                android:id="@+id/anime_name_txt"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:gravity="center"
                android:textStyle="bold"
                android:textSize="15sp"
                android:layout_weight="3"/>
    
            <TextView android:id="@+id/anime_author_txt"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:gravity="center"
                android:textStyle="italic"
                android:layout_weight="2"
                android:textSize="10sp"/>
            
            <View
                android:layout_width="match_parent"
                android:layout_height="3dp" />
        </LinearLayout>
    </RelativeLayout>

    常识1:android:layout_weight分配的大小=(父控件大小-android:layout_width)*权重比例大小

    如果android:layout_width的大小已经达到或超过父控件的大小,则android:layout_weight是会失效的。

     

    BaseAdapterActivity.java, 适配器的使用,代码如下

    package com.flx.adaptertest.baseadapter;
    
    import android.app.Activity;
    import android.content.res.Resources;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.AdapterView;
    import android.widget.CheckBox;
    import android.widget.GridView;
    import android.widget.ListView;
    import android.widget.SimpleAdapter;
    
    import com.flx.adaptertest.R;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class BaseAdapterActivity extends Activity {
        private static final String TAG = "BaseAdapterActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate( savedInstanceState );
            setContentView( R.layout.base_adapter_act );
            GridView gridView = findViewById(R.id.base_adapter_lv);
            gridView.setNumColumns(2);
            gridView.setOnItemClickListener( new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    Log.d( TAG, "onItemClick: position="+ position + ";text=" + parent.getAdapter().getItem(position).toString());
                }
            } );
    
            List<AnimeBean> animeBeans = new ArrayList<>();
            Resources resources = this.getResources();
            String[] anime_names = resources.getStringArray( R.array.anime_name );
            String[] anime_authors = resources.getStringArray( R.array.anime_author );
            int[] coverImgs = {R.drawable.hzw1, R.drawable.jjdjr1, R.drawable.hyrz1,
                    R.drawable.zchzt1, R.drawable.qsmy1, R.drawable.xyj1, R.drawable.hlw1};
            for (int i = 0; i < anime_names.length; i++) {
                animeBeans.add(new AnimeBean(anime_names[i], anime_authors[i], coverImgs[i]));
            }
            gridView.setAdapter( new AnimeAdapter(this, animeBeans) );
        }
    }

    这里创建了两个类,AnimeBean和AnimeAdapter。 

    AnimeBean是一个java bean类,数据以Bean类组织,其中主要是Getter和Setter的方法。适配器显示的每一项数据在一个AnimeBean对象中。

    AnimeAdapter是继承BaseAdapter的自定义的适配器类,实现了上述讲到的4个方法。

     

    AnimeBean.java

    package com.flx.adaptertest.baseadapter;
    
    import android.support.annotation.NonNull;
    
    public class AnimeBean {
        private String mAnimeName;
        private String mAnimeAuthor;
        private int mAnimeCoverImg;
    
        public AnimeBean(String animeName, String animeAuthor, int animeCoverImg) {
            this.mAnimeName = animeName;
            this.mAnimeAuthor = animeAuthor;
            this.mAnimeCoverImg = animeCoverImg;
        }
    
        public String getmAnimeName() {
            return this.mAnimeName;
        }
    
        public void setmAnimeName(String animeName) {
            this.mAnimeName = animeName;
        }
    
        public String getmAnimeAuthor() {
            return this.mAnimeAuthor;
        }
    
        public void setmAnimeAuthor(String animeAuthor) {
            this.mAnimeAuthor = animeAuthor;
        }
    
        public int getmAnimeCoverImg() {
            return this.mAnimeCoverImg;
        }
    
        public void setmAnimeCoverImg(int animeCoverImg) {
            this.mAnimeCoverImg = animeCoverImg;
        }
    
        @NonNull
        @Override
        public String toString() {
            return "mAnimeName:"+mAnimeName+";mAnimeAuthor:"+mAnimeAuthor+";mAnimeCoverImg:"+mAnimeCoverImg;
        }
    }

    最后一个toString用户打印Bean里面数据的内容,在BaseAdapterActivity的适配器点击项监听处有调用。

    AnimeAdapter.java

    package com.flx.adaptertest.baseadapter;
    
    import android.content.Context;
    import android.util.Log;
    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 com.flx.adaptertest.R;
    import java.util.List;
    
    public class AnimeAdapter extends BaseAdapter {
        private static final String TAG = "AnimeAdapter";
        private LayoutInflater mLayoutInflater;//布局加载器对象
        private List<AnimeBean> mAnimeBeans;//数据源
    
        //构造方法,包含了数据源和上下文。将数据和适配器关联起来了。
        public AnimeAdapter(Context context, List<AnimeBean> animeBeans) {
            mLayoutInflater =  LayoutInflater.from(context);
            mAnimeBeans = animeBeans;
        }
    
        //item的数目,即适配器要显示的数据项个数
        @Override
        public int getCount() {
            return mAnimeBeans != null ? mAnimeBeans.size() : 0;
        }
    
        //获取指定位置的数据项
        @Override
        public Object getItem(int position) {
            return mAnimeBeans != null ? mAnimeBeans.get(position) : null;
        }
    
        //获取指定位置的数据项id
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        //获取每一项显示内容-view
        /**
         *方法3:
         * 这种方法改进了方法1和方法2的弊端,避免了每次都创建新的View对象和通过findViewById查找组件 而造成的耗时 耗资源的问题。
         * 创建View对象通过判空避免了:if (convertView == null)
         * findViewById()通过ViewHolder缓存下来了,通过setTag设置到View了,需要时可以直接获取到。
         */
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            //convertView是显示项的视图。为null时即表示未被实例化过,GridView缓存池中没有缓存
            Log.d( TAG, "getView: position=" + position +";convertView="+convertView );
            if (convertView == null) {
                convertView = mLayoutInflater.inflate( R.layout.base_adapter_grid_item, null );
                //创建ViewHolder对象,并赋值
                viewHolder = new ViewHolder();
                viewHolder.animeNameTxt = convertView.findViewById(R.id.anime_name_txt);
                viewHolder.animeAuthorTxt = convertView.findViewById(R.id.anime_author_txt);
                viewHolder.animeCoverImg = convertView.findViewById(R.id.anime_cover_img);
                //通过setTag,设置与convertView关联的标签ViewHolder,将convertView与ViewHolder关联
                convertView.setTag(viewHolder);
            } else {
                //从缓存中返回convertView设置的标签:ViewHolder
                viewHolder = (ViewHolder) convertView.getTag();
            }
    
            AnimeBean animeBean = mAnimeBeans.get(position);
            viewHolder.animeNameTxt.setText(animeBean.getmAnimeName());
            viewHolder.animeAuthorTxt.setText(animeBean.getmAnimeAuthor());
            viewHolder.animeCoverImg.setImageResource(animeBean.getmAnimeCoverImg());
            return convertView;
        }
    
        //缓存控件
        private class ViewHolder {
            public TextView animeNameTxt;
            public TextView animeAuthorTxt;
            public ImageView animeCoverImg;
        }
    
        /**
         * 方法1:
         * 这种方法的弊端在于:很明显,每次都会创建新的convertView对象,并通过findViewById查找对应组件。
         * 当数据项很大且比较复杂时问题很明显,耗时 耗资源
         * 没有使用GridView ListView的缓存机制。
         */
       /* @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            convertView = mLayoutInflater.inflate( R.layout.base_adapter_grid_item, null );
            TextView animeNameTxt = convertView.findViewById(R.id.anime_name_txt);
            TextView animeAuthorTxt = convertView.findViewById(R.id.anime_author_txt);
            ImageView animeCoverImg = convertView.findViewById(R.id.anime_cover_img);
    
            AnimeBean animeBean = mAnimeBeans.get(position);
            animeNameTxt.setText(animeBean.getmAnimeName());
            animeAuthorTxt.setText(animeBean.getmAnimeAuthor());
            animeCoverImg.setImageResource(animeBean.getmAnimeCoverImg());
            return convertView;
        }*/
    
        /**
         * 方法2:
         * 该方法时方法1的改进,使用了缓存机制。
         * 这种方法的弊端在于:每次都会通过findViewById()去遍历视图树,当布局很复杂时就会很耗时
         */
        /*@Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = mLayoutInflater.inflate( R.layout.base_adapter_grid_item, null );
            }
            TextView animeNameTxt = convertView.findViewById(R.id.anime_name_txt);
            TextView animeAuthorTxt = convertView.findViewById(R.id.anime_author_txt);
            ImageView animeCoverImg = convertView.findViewById(R.id.anime_cover_img);
    
            AnimeBean animeBean = mAnimeBeans.get(position);
            animeNameTxt.setText(animeBean.getmAnimeName());
            animeAuthorTxt.setText(animeBean.getmAnimeAuthor());
            animeCoverImg.setImageResource(animeBean.getmAnimeCoverImg());
            return convertView;
        }*/
    
    }

    代码中注释做了详细说明,需要注意几点:

    1.自定义的Adapter继承了BaseAdapter,需要实现最初讲到的4个方法。getCount()、getItem()、getItemId()都比较简单,注意getView()的实现。

    2.GridView、ListView等是由缓存机制的,当需要显示的时候才会显示,不需要显示的时候在缓存池中。当要显示的信息过多,超过屏幕很多,滑出的信息和滑入的信息 都是从缓存池中获取的,缓存池中的信息不会创建所有或马上销毁。(最后部分有将数据调整为1000后 也能看出这一点)

    3.上述代码中,getView()列出了3种方法,方法1、方法2有各自明显缺陷,方法3是一个比较好的方法很好的避免了每次都创建新的View对象和通过findViewById查找组件 而造成的耗时 耗资源的问题。具体请看上述代码和注释。

     示例效果

    下面是效果图

    点击其中一项,log如下:

    2019-11-27 14:36:33.949 4655-4655/? D/BaseAdapterActivity: onItemClick: 
    position=0;text=mAnimeName:海贼王;mAnimeAuthor:尾田荣一郎;mAnimeCoverImg:2131165272
    

    要看下getView() 方法3的效果,可以将数据调整为1000组,可以在BaseAdapterActivity中做如下修改

    //        for (int i = 0; i < anime_names.length; i++) {
    //            animeBeans.add(new AnimeBean(anime_names[i], anime_authors[i], coverImgs[i]));
    //        }
            for (int i = 0; i < 1000; i++) {
                int ii = i%anime_names.length;
                animeBeans.add(new AnimeBean(anime_names[ii], anime_authors[ii], coverImgs[ii]));
            }

    后面大部分View是没有创建的,向下滑动 才会逐步创建,如下的log,

    滑动逐步显示后面的信息,后面信息都不需要每次创建View而是通过viewHolder = (ViewHolder) convertView.getTag()从缓存中获取来的,然后填充数据即可。

     

  • 相关阅读:
    我的第一篇博客
    吴裕雄--天生自然python学习笔记:Python3 标准库概览
    吴裕雄--天生自然python学习笔记:Python3 命名空间和作用域
    吴裕雄--天生自然python学习笔记:Python3 面向对象
    吴裕雄--天生自然python学习笔记:Python3 错误和异常
    吴裕雄--天生自然python学习笔记:Python3 OS 文件/目录方法
    吴裕雄--天生自然python学习笔记:Python3 File(文件) 方法
    吴裕雄--天生自然python学习笔记:Python3 输入和输出
    吴裕雄--天生自然python学习笔记:Python3 模块
    吴裕雄--天生自然python学习笔记:Python3 数据结构
  • 原文地址:https://www.cnblogs.com/fanglongxiang/p/11938431.html
Copyright © 2011-2022 走看看