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()从缓存中获取来的,然后填充数据即可。

     

  • 相关阅读:
    phpcms后台进入地址(包含No permission resources错误)
    phpmyadmin上传大sql文件办法
    ubuntu彻底卸载mysql
    Hdoj 2602.Bone Collector 题解
    一篇看懂词向量
    Hdoj 1905.Pseudoprime numbers 题解
    The Python Challenge 谜题全解(持续更新)
    Hdoj 2289.Cup 题解
    Hdoj 2899.Strange fuction 题解
    Hdoj 2199.Can you solve this equation? 题解
  • 原文地址:https://www.cnblogs.com/fanglongxiang/p/11938431.html
Copyright © 2011-2022 走看看