ListView优化之一:重用convertView

来源:互联网 发布:怎么报考网络教育 编辑:程序博客网 时间:2024/04/28 04:13

【ListView优化方式有多种多样,而我们本文作为ListView优化系列文章的第一篇文章,欢迎大家持续关注ListView优化系列文章并关注Jimy的博客。有误的地方,希望大家能多多指正。】

ListView优化之一:重用convertView

重用convertVIew,很大程度上减少了Android设备内存的消耗。

(通过判断convertView是否为null,是的话就需要产生一个view出来,然后给这个视图数据,最后将这个视图返回给底层,呈现给用户)。

先上效果图:


由于本文主要是在描述如何重用contertView来优化ListView,因此本文不会过多的描述其他内容,会直接使用到Gson解析json数据、第三方框架OkHttp。给各位推荐下一个格式化json数据的网址:http://www.bejson.com

(通过判断convertView是否为null,是的话就需要产生一个view出来,然后给这个视图数据,最后将这个视图返回给底层,呈现给用户)。

【注:案例是在Android Studio上运行,这样的话,添加依赖或者说导入类库相比于Eclipse就会方便快捷很多。】

首先来看一下demo的包是如何建的,如下图:


1.首先,我们来看一下需要添加的依赖包,我们需要在builde文件中添加如下两个依赖:

compile 'com.squareup.okhttp3:okhttp:3.3.0'compile 'com.google.code.gson:gson:2.6.2'

然后就可以进行代码编写了。

2.我们来看一下工具类OkHttpUtils,需要提醒大家的是本文中并没有使用到Post方式请求网络数据,而是只使用了Get请求,不过在这轮系列博文中是会使用到Post方式请求网络数据的,欢迎大家继续关注。

OkHttpUtils类代码如下:

package com.jimy.convertview.util;import android.os.Handler;import android.os.Looper;import java.io.IOException;import java.util.Map;import java.util.concurrent.TimeUnit;import okhttp3.Call;import okhttp3.Callback;import okhttp3.FormBody;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.Response;/** * OkHttp的工具类 */public class OkHttpUtils{    private static OkHttpClient okHttpClient;    private static Handler handler = new Handler(Looper.getMainLooper());    /**     * 初始化OkHttpUtils     */    public static void initOkHttp()    {        okHttpClient = new OkHttpClient.Builder()                .connectTimeout(10, TimeUnit.SECONDS)                .readTimeout(10, TimeUnit.SECONDS)                .writeTimeout(10, TimeUnit.SECONDS)                .build();    }    /**     * 异步Get请求     *     * @param url     */    public static void getSubmitForm(final String url, final OnGetDataListener onGetDataListener)    {        Request request = new Request.Builder()                .url(url)                .build();        Call call = okHttpClient.newCall(request);        call.enqueue(new Callback()        {            @Override            public void onFailure(Call call, IOException e)            {                if (onGetDataListener != null)                {                    onGetDataListener.onFailure(url, e.getMessage());                }            }            @Override            public void onResponse(Call call, final Response response) throws IOException            {                final String str = response.body().string();                handler.post(new Runnable()                {                    @Override                    public void run()                    {                        if (onGetDataListener != null)                        {                            onGetDataListener.onResponse(url, str);                        }                    }                });            }        });    }    /**     * Post提交键值对参数     */    public static void postSubmitForm(final String url, Map<String, Object> params,            final OnGetDataListener onGetDataListener)    {        FormBody.Builder builder = new FormBody.Builder();        if (params != null && params.size() > 0)        {            for (String key : params.keySet())            {                String value = String.valueOf(params.get(key));                builder.add(key, value);            }        }        FormBody formBody = builder.build();        final Request request = new Request.Builder()                .url(url)                .post(formBody)                .build();        okHttpClient.newCall(request).enqueue(new Callback()        {            @Override            public void onFailure(Call call, IOException e)            {                if (onGetDataListener != null)                {                    onGetDataListener.onFailure(url, e.getMessage());                }            }            @Override            public void onResponse(Call call, final Response response) throws IOException            {                final String str = response.body().string();                handler.post(new Runnable()                {                    @Override                    public void run()                    {                        if (onGetDataListener != null)                        {                            onGetDataListener.onResponse(url, str);                        }                    }                });            }        });    }    public interface OnGetDataListener    {        void onResponse(String url, String strs);        void onFailure(String url, String error);    }}

3.为了方便初始化OkHttp并且使我们的代码复用性更强,在这里我们需要创建一个BaseApplication类来继承Application,主要是为了我们的App在启动时直接初始化更多的第三方框架,也为了方便我们在不同的Activity或Fragment等中使用这些第三方框架。

BaseApplication类代码如下:

package com.jimy.convertview.base;import android.app.Application;import com.jimy.convertview.util.OkHttpUtils;public class BaseApplication extends Application {    private static BaseApplication application;    @Override    public void onCreate() {        super.onCreate();        application = this;        // 初始化OkHttpUtils        OkHttpUtils.initOkHttp();    }    public static BaseApplication getApplication(){        return application;    }}

当然,重写了继承Application的BaseApplication类,我们需要在AndroidManifest.xml文件中添加name属性。同时OkHttp需要进行联网加载网络数据,所以还需要添加网络权限。如下:

//网络权限
<uses-permission android:name="android.permission.INTERNET"/>
<application    android:name=".base.BaseApplication">
</application>

4.我们单独在这里加了一个包来放常量Url,这可以让程序一目了然、十分清晰。

RequestUrl类代码如下:(只是存了一个Url地址,请注意本文中使用的pageSize=50,在ListView优化的后续博文中,我们还会对这个数据进行修改优化,欢迎继续关注)

package com.jimy.convertview.urls;public class RequestUrl {    public static final String ENTITY_URL =            "http://mrobot.pcauto.com.cn/v2/cms/channels/2?pageNo=1&pageSize=50&v=4.0.0";}
有了上面的Url地址,我们可以进行联网获得json数据,然后完全复制这些原始数据,并放入到http://www.bejson.com网址下进行格式化,便于我们查看信息内容。这时,我们通过Android Studio里的GsonFormat插件(这个插件相信大家肯定都安装了吧?功能很强大,给我们写代码省了好多时间。)来自动生成ListViewEntity类,这各类就略过不详细描述咯(太简单了,都会的)。

5.有了上边所有的这些内容,相信你已经迫不及待想要继续编写出正式的代码了吧。好的,接下来我们就来叙述我们的Activity。在我们的Activity中,我们需要显示我们的ListView的item中数据,并需要使用到OkHttpUtils工具类。

MainAcity类代码如下:

package com.jimy.convertview.activity;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.widget.ListView;import com.google.gson.Gson;import com.jimy.convertview.R;import com.jimy.convertview.adapter.ListItemLayoutAdapter;import com.jimy.convertview.entity.ListViewEntity;import com.jimy.convertview.urls.RequestUrl;import com.jimy.convertview.util.OkHttpUtils;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity {    private ListView mListView;    private List<ListViewEntity.DataBean> data;    private ListItemLayoutAdapter mAdapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //初始化视图控件        initView();        //加载数据        loadData();    }    private void loadData() {        OkHttpUtils.getSubmitForm(RequestUrl.ENTITY_URL, new OkHttpUtils.OnGetDataListener() {            @Override            public void onResponse(String url, String strs) {                //创建一个方法来显示数据                showListItemInfoView(strs);            }            @Override            public void onFailure(String url, String error) {                //当数据加载失败时,我们在这里进行打印日志输出url,异常信息error                Log.d("MainActivity", url);                Log.d("MainActivity", error);            }        });    }    private void showListItemInfoView(String strs) {        Gson gson = new Gson();        //gson解析数据        ListViewEntity listViewEntity = gson.fromJson(strs, ListViewEntity.class);        data.addAll(listViewEntity.getData());        //对数据进行刷新显示        mAdapter.notifyDataSetChanged();    }    private void initView() {        mListView = (ListView) findViewById(R.id.listView);        data = new ArrayList<>();        mAdapter = new ListItemLayoutAdapter(this, data);        mListView.setAdapter(mAdapter);    }}
在activity_main.xml文件中我们只显示了一个ListView,十分简单,如下:

<?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:layout_marginRight="10dp"    android:layout_marginLeft="10dp"    android:orientation="vertical">    <ListView        android:id="@+id/listView"        android:layout_width="match_parent"        android:layout_height="match_parent"/></LinearLayout>

【以上所有代码都是为我们接下来的ListView来优化而写的代码,在这轮ListView系列博文当中,我们会重复使用这里的代码,欢迎继续关注Jimy的博客!】

6.最重要的部分马上显现了,请稍后,莫急。

让我们来先用一张图来描述一下,适配器Adapter中被重复调用的getView(int position, View convertView, ViewGrop parent)方法。相信看了这张图,你能很清晰的了解为何ListView的优化通过重用convertView也能达到目的。如下图所示:


我们需要重复调用getView()这个方法。

接下来我们来看一看我们的Adapter,在这里,我们是直接使用泛型T来写的Adapter适配器,这样就有助于我们以后能持续的重复使用这个Adapter。在这里,我们还没有把图片加载进去,因为ListView的图片加载显示也是ListView优化方式之一,我们会在后续文章中提到,暂时就不过多深究这个问题了。

ListItemLayoutAdapter类代码如下:

package com.jimy.convertview.adapter;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 com.jimy.convertview.R;import com.jimy.convertview.entity.ListViewEntity;import java.text.SimpleDateFormat;import java.util.List;/** * 使用泛型来适配ListViewitem内容,方便重用Adapter * @param <T> */public class ListItemLayoutAdapter<T> extends BaseAdapter {    private List<T> objects;    private Context context;    private LayoutInflater layoutInflater;    public ListItemLayoutAdapter(Context context, List<T> objects) {        this.context = context;        this.layoutInflater = LayoutInflater.from(context);        this.objects = objects;    }    @Override    public int getCount() {        return objects.size();    }    @Override    public T getItem(int position) {        return objects.get(position);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        ViewHolder viewHolder = null;        //要重用convertView,首先判断convertView是否null        if (convertView == null) {            convertView = layoutInflater.inflate(R.layout.list_item_layout, null);            viewHolder = new ViewHolder(convertView);            convertView.setTag(viewHolder);        }else {            viewHolder = (ViewHolder) convertView.getTag();        }        //使用的泛型T        Object obj = getItem(position);        //在这里需要判断泛型的具体对象类型        if (obj instanceof ListViewEntity.DataBean){            ListViewEntity.DataBean entity = (ListViewEntity.DataBean) obj;            //设置标题            viewHolder.tvTitle.setText(entity.getTitle());            //设置评论次数            viewHolder.tvCount.setText(entity.getCount()+"评论");            //设置时间            /**             * 从网络上加载的时间是long类型的毫秒数             *  因此需要通过SimpleDateFormat来进行转换为特定的格式             *   在这里我们直接转换为yyyy-MM-dd的格式类型             */            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");            String timeStr = format.format(entity.getMtime());            viewHolder.tvTime.setText(timeStr);        }        return convertView;    }    protected class ViewHolder {        private ImageView imgPic;        private TextView tvTitle;        private TextView tvCount;        private TextView tvTime;        public ViewHolder(View view) {            imgPic = (ImageView) view.findViewById(R.id.img_pic);            tvTitle = (TextView) view.findViewById(R.id.tv_title);            tvCount = (TextView) view.findViewById(R.id.tv_count);            tvTime = (TextView) view.findViewById(R.id.tv_time);        }    }}
这里的list_item_layout.xml文件就是ListView的item要显示的内容了,主要有一个ImgeView、三个TextView排布而成的。(为了方便,我们就直接使用的LinearLayout布局方式,在这里我们暂时不考虑LinearLayout的多层嵌套而造成的影响,只需要达到我们显示的效果即可。【注:多层LinearLayout是会影响到程序运行的效率的,这时候我们应该使用RelativeLayout布局进行加载会快很多。这里就暂时不描述了】)。

list_item_layout.xml代码如下:

<?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="110dp"    android:layout_marginBottom="10dp"    android:orientation="vertical">    <LinearLayout        android:layout_width="match_parent"        android:layout_height="100dp"        android:orientation="horizontal">        <ImageView            android:id="@+id/img_pic"            android:layout_width="100dp"            android:layout_height="match_parent"            android:scaleType="centerCrop"            android:src="@mipmap/img_bg" />        <LinearLayout            android:layout_width="match_parent"            android:layout_height="match_parent"            android:layout_marginLeft="10dp"            android:orientation="vertical">            <TextView                android:id="@+id/tv_title"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_marginTop="20dp"                android:text="Jimy的博客"                android:textColor="@android:color/black"                android:textSize="16sp" />            <LinearLayout                android:layout_width="match_parent"                android:layout_height="match_parent"                android:gravity="bottom"                android:orientation="horizontal">                <TextView                    android:id="@+id/tv_count"                    android:layout_width="wrap_content"                    android:layout_height="wrap_content"                    android:text="200评论" />                <LinearLayout                    android:layout_width="match_parent"                    android:layout_height="wrap_content"                    android:gravity="right"                    android:orientation="horizontal">                    <TextView                        android:id="@+id/tv_time"                        android:layout_width="wrap_content"                        android:layout_height="wrap_content"                        android:text="yyyy-MM-dd" />                </LinearLayout>            </LinearLayout>        </LinearLayout>    </LinearLayout></LinearLayout>

ok,我们的ListView优化之一:重用convertView,就暂时讲到这里了,后续的ListView优化还会持续以博文的形式分享给大家,希望多多关注Jimy的博客。


1 0
原创粉丝点击