前言

之前写过一篇文章是关于搜索框的封装( Android通用的搜索框),当时只是对搜索栏部分进行简单的封装,把搜索逻辑、页面和数据处理封装了一下,而搜索页的处理(包括数据源,控件等)还需要手动的去创建和修改,用起来还是比较麻烦,最近不太忙,刚好需要用到这部分内容,于是又进一步的封装了一下.(文章只适用于数据量不太大的本地搜索,例如通讯录,城市列表,本地订单等)

还是先看一下效果图吧

android搜索框 弹出动画 安卓搜索页面_数据

主要看一下封装的实现.


分析与实现

先看一下之前写的搜索页代码(没看过( Android通用的搜索框)的童鞋,可以先大概看一下)

public class MainActivity extends AppCompatActivity {
    /**
     * TAG
     */
    private static final String TAG = "MainActivity";
    /**
     * 通用搜索框
     */
    private CommolySearchView<SearchBean> mCommolySearchView;
    /**
     * 数据显示listview
     */
    private ListView mListView;
    /**
     * 数据源
     */
    private List<SearchBean> mDatas;
    /**
     * 适配器
     */
    private SearchAdapter adapter;
    //===========================三国部分===========================//
    /**
     * 三国通用搜索框
     */
    private CommolySearchView<SanGuoBean> mSGCommolySearchView;
    /**
     * 三国数据源
     */
    private List<SanGuoBean> mSGDatas;
    /**
     * 三国适配器
     */
    private SGAdapter sgAdapter;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
//        initData();
//        initView();
        initDataI();
        initViewI();
    }

    /**
     * 初始化数据
     */
    private void initData() {
        mDatas = new ArrayList<SearchBean>();
        SearchBean bean1 = new SearchBean();
        bean1.setSearchName("小明");
        bean1.setSearchDescribe("一年级二班");
        ...
        mDatas.add(bean1);
        ...
        mDatas.add(bean7);
    }

    /**
     * 初始化控件
     */
    private void initView() {
        mCommolySearchView = (CommolySearchView) findViewById(R.id.csv_show);
        mListView = (ListView) findViewById(R.id.lv_show);
        adapter = new SearchAdapter(this, mDatas);
        mListView.setAdapter(adapter);
        // 设置数据源
        mCommolySearchView.setDatas(mDatas);
        // 设置适配器
        mCommolySearchView.setAdapter(adapter);
        // 设置筛选数据
        mCommolySearchView.setSearchDataListener(new CommolySearchView.SearchDatas<SearchBean>() {

            @Override
            public List<SearchBean> filterDatas(List<SearchBean> datas, List<SearchBean> filterdatas, String inputstr) {
                Log.e(TAG, "filterDatas: " + inputstr);
                for (int i = 0; i < datas.size(); i++) {
                    // 筛选条件
                    if ((datas.get(i).getSearchName()).contains(inputstr) || datas.get(i).getSearchDescribe().contains(inputstr)) {
                        filterdatas.add(datas.get(i));
                    }
                }
                return filterdatas;
            }
        });
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                Toast.makeText(MainActivity.this, "当前点击的是" + mCommolySearchView.getFilterDatas().get(i).getSearchName(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    /**
     * 初始化数据
     */
    private void initDataI() {
        mSGDatas = new ArrayList<SanGuoBean>();
        SanGuoBean sgbean1 = new SanGuoBean();
        sgbean1.setSgName("刘备");
        sgbean1.setSgPetName("玄德");
        sgbean1.setSgHeadBp(R.drawable.lb);
        sgbean1.setSgDescribe("刘备(161年-223年6月10日),字玄德,东汉末年幽州涿郡涿县(今河北省涿州市)人");
        ... 
        mSGDatas.add(sgbean1);
        ...
        mSGDatas.add(sgbean7);

    }

    /**
     * 初始化控件
     */
    private void initViewI() {
        mSGCommolySearchView = (CommolySearchView) findViewById(R.id.csv_show);
        mListView = (ListView) findViewById(R.id.lv_show);
        sgAdapter = new SGAdapter(this, mSGDatas);
        mListView.setAdapter(sgAdapter);
        // 设置数据源
        mSGCommolySearchView.setDatas(mSGDatas);
        // 设置适配器
        mSGCommolySearchView.setAdapter(sgAdapter);
        // 设置筛选数据
        mSGCommolySearchView.setSearchDataListener(new CommolySearchView.SearchDatas<SanGuoBean>() {
            @Override
            public List<SanGuoBean> filterDatas(List<SanGuoBean> datas, List<SanGuoBean> filterdatas, String inputstr) {
                for (int i = 0; i < datas.size(); i++) {
                    // 筛选条件
                    if ((datas.get(i).getSgDescribe()).contains(inputstr) || datas.get(i).getSgName().contains(inputstr) || datas.get(i).getSgPetName().contains(inputstr)) {
                        filterdatas.add(datas.get(i));
                    }
                }
                return filterdatas;
            }
        });
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
              // 点击操作
              ...
            }
        });

    }

}

为了节省篇幅,省略了一些初始化数据的内容.

分析一下那些地方是需要进行优化和封装的.
一)可以看到当搜索不同内容时,需要创建不同的List来接收不同类型的数据源

/**
     * 数据源1
     */
    private List<SearchBean> mDatas;
    /**
     * 三国数据源(数据源2)
     */
    private List<SanGuoBean> mSGDatas;

二)通用搜索栏的实现中为了通用性,使用了泛型,所以每次定义CommolySearchView时需要传入一个泛型,来规定引用类型

/**
     * 数据源1的搜索栏
     */
    private CommolySearchView<SearchBean> mCommolySearchView;
     /**
     * 三国数据源通用搜索框(数据源2)
     */
    private CommolySearchView<SanGuoBean> mSGCommolySearchView;

三)当然使用listview时,不同的数据源对应着不同的适配器

/**
     * 数据源1适配器
     */
    private SearchAdapter adapter;
    /**
     * 三国适配器(数据源2适配器)
     */
    private SGAdapter sgAdapter;

针对这三点,来分析一下
先来看第一点,关于不同数据源的问题.看到这个问题,脑海里第一个想到的就是使用泛型,不过要如何设计和使用呢.直接在搜索页定义一个list< T > 来接收不同的数据源并规定引用类型?显然不合适.最好是有一个中介. 这里定义一个CommonSearchBean来传递和使用不同类型的数据源

package com.example.junweiliu.commonlysearchview.bean;

import java.io.Serializable;
import java.util.List;

/**
 * Created by junweiliu on 17/3/29.
 * 封装的搜索bean用于传递和使用不同类型的数据源
 */
public class CommonSearchBean<T> implements Serializable {
    /**
     * 数据源
     */
    private List<T> list;
    /**
     * 搜索类型  1代表搜索测试1,2代表搜索测试2
     */
    private int searchType;

    public List<T> getList() {
        return list;
    }

    public void setList(List<T> list) {
        this.list = list;
    }

    public int getSearchType() {
        return searchType;
    }

    public void setSearchType(int searchType) {
        this.searchType = searchType;
    }
}

这里有一个searchType,是为了区分不同的搜索类型,之后会用到.这样数据源传入和使用就可以通过CommonSearchBean来完成

// 传输数据
 CommonSearchBean commonSearchBean = new CommonSearchBean();
 // 设置数据
 commonSearchBean.setList(mDatas);
 ...
 //---------------------分割线------------------------//
 // 获取数据
 mDatas = commonSearchBean.getList();
 ...

再来看一下第二点,关于通用搜索栏,怎么再进一步的优化和封装呢?使用通用搜索栏时需要传入一个泛型< T >去规定引用类型,但实际并不关心引用类型是什么,只是需要在搜索回调中,把符合筛选条件的数据返回即可,相关代码

// 设置筛选数据
        mSGCommolySearchView.setSearchDataListener(new CommolySearchView.SearchDatas<SanGuoBean>() {
            @Override
            public List<SanGuoBean> filterDatas(List<SanGuoBean> datas, List<SanGuoBean> filterdatas, String inputstr) {
                for (int i = 0; i < datas.size(); i++) {
                    // 筛选条件
                    if ((datas.get(i).getSgDescribe()).contains(inputstr) || datas.get(i).getSgName().contains(inputstr) || datas.get(i).getSgPetName().contains(inputstr)) {
                        filterdatas.add(datas.get(i));
                    }
                }
                return filterdatas;
            }
        });

大多数的搜索都是使用包含条件,所以这里规定一个接口BaseSearch,通过getSearchCondition()方法返回筛选条件

package com.example.junweiliu.commonlysearchview.bean;

/**
 * Created by junweiliu on 17/3/29.
 */
public interface BaseSearch {
    /**
     * 获取筛选条件
     *
     * @return
     */
    String getSearchCondition();
}

在相关实体bean中实现该接口,例如

package com.example.junweiliu.commonlysearchview.bean;

import java.io.Serializable;

/**
 * Created by junweiliu on 17/3/29.
 */
public class SearchDemoBeanTwo implements BaseSearch, Serializable {
    /**
     * 头像资源
     */
    private int iconRes;
    /**
     * 头像地址
     */
    private int iconUrl;
    /**
     * 姓名
     */
    private String name;
    /**
     * 电话
     */
    private String phone;
    /**
     * 邮箱
     */
    private String email;

    public int getIconRes() {
        return iconRes;
    }

    public void setIconRes(int iconRes) {
        this.iconRes = iconRes;
    }

    public int getIconUrl() {
        return iconUrl;
    }

    public void setIconUrl(int iconUrl) {
        this.iconUrl = iconUrl;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    /**
     * 搜索条件
     *
     * @return
     */
    @Override
    public String getSearchCondition() {
        StringBuilder searchStr = new StringBuilder();
        searchStr.append(name);
        searchStr.append(phone);
        searchStr.append(email);
        return searchStr.toString();
    }
}

把筛选条件合并(当然可以根据需求返回筛选条件,因为需要修改字符串,所以这里使用StringBuilder,效率比较高),通过getSearchCondition()方法返回.之后在搜索页就可以这样写

/**
     * 搜索框
     */
    CommolySearchView<BaseSearch> mCsvShow;

    ...

    // 设置搜索
    mCsvShow.setSearchDataListener(new CommolySearchView.SearchDatas<BaseSearch>() {
            @Override
            public List<BaseSearch> filterDatas(List<BaseSearch> datas, List<BaseSearch> filterdatas, String inputstr) {
                for (int i = 0; i < datas.size(); i++) {
                    // 筛选条件,如果有必要,在此做修改
                    if (datas.get(i).getSearchCondition().contains(inputstr)) {
                        filterdatas.add(datas.get(i));
                    }
                }
                return filterdatas;
            }
        });

最后看一下第三个问题,不同的适配器.适配器初始化一般都需要上下文,数据源,布局文件资源.数据源的问题在第一个问题中已经解决,所以在搜索页定义一个BaseAdapter对象,再根据搜索类型实例化不同的对象即可,相关代码如下

/**
     * 适配器
     */
    private BaseAdapter mAdapter;
    /**
     * 封装的搜索bean
     */
    private CommonSearchBean mCommonSearchBean;
    /**
     * 搜索类型(用于区分不同的搜索)
     */
    private int searchType;

    ...

    if (1 == searchType) {              // 测试例子1
            // 适配器1
            mAdapter = new SearchDemoAapterOne(this, mCommonSearchBean.getList(), R.layout.item_search_one);
    } else if (2 == searchType) {       // 测试例子2
            // 适配器2
            mAdapter = new SearchDemoAdapterTwo(this, mCommonSearchBean.getList());
    }

细心的童鞋可能注意到在定义CommonSearchBean时没有规定引用类型,这里不规定引用类型的原因就是为了通用性,当使用泛型不规定引用类型时,默认为Object,在适配器初始化时传入的数据源通过CommonSearchBean.getList()方法获得,只需要保证setList()方法中的数据类型和getList()方法中的数据类型一致即可.

具体代码

最后看一下主要的代码

搜索页CommonSearchActivity

package com.example.junweiliu.commonlysearchview.activity;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.Toast;

import com.example.junweiliu.commonlysearchview.R;
import com.example.junweiliu.commonlysearchview.adapter.SearchDemoAapterOne;
import com.example.junweiliu.commonlysearchview.adapter.SearchDemoAdapterTwo;
import com.example.junweiliu.commonlysearchview.bean.BaseSearch;
import com.example.junweiliu.commonlysearchview.bean.CommonSearchBean;
import com.example.junweiliu.commonlysearchview.bean.SearchDemoBeanOne;
import com.example.junweiliu.commonlysearchview.bean.SearchDemoBeanTwo;
import com.example.junweiliu.commonlysearchview.widget.CommolySearchView;

import java.util.List;


/**
 * Created by junweiliu on 17/3/29.
 */
public class CommonSearchActivity extends AppCompatActivity {
    /**
     * 搜索框
     */
    CommolySearchView<BaseSearch> mCsvShow;
    /**
     * 数据展示
     */
    ListView mLvShow;
    /**
     * 适配器
     */
    private BaseAdapter mAdapter;
    /**
     * 封装的搜索bean
     */
    private CommonSearchBean mCommonSearchBean = new CommonSearchBean();
    /**
     * 搜索类型(用于区分不同的搜索)
     */
    private int searchType;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_common_search);
        initData();
        initView();
        initAdapter();
        initSearch();
    }


    /**
     * 初始化适配器,一般的扩展只需修改该方法即可
     */
    private void initAdapter() {
        if (1 == searchType) {              // 测试例子1
            // 适配器1
            mAdapter = new SearchDemoAapterOne(this, mCommonSearchBean.getList(), R.layout.item_search_one);
            // 点击事件
            mLvShow.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    Toast.makeText(CommonSearchActivity.this, "点击的是:" + ((SearchDemoBeanOne) mCommonSearchBean.getList().get(position)).getName(), Toast.LENGTH_SHORT).show();
                }
            });
        } else if (2 == searchType) {       // 测试例子2
            // 适配器2
            mAdapter = new SearchDemoAdapterTwo(this, mCommonSearchBean.getList());
            // 点击事件
            mLvShow.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    Toast.makeText(CommonSearchActivity.this, "点击的是:" + ((SearchDemoBeanTwo) mCommonSearchBean.getList().get(position)).getName(), Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

    /**
     * 初始化数据
     */
    private void initData() {
        mCommonSearchBean = (CommonSearchBean) getIntent().getSerializableExtra("seachBean");
        searchType = mCommonSearchBean.getSearchType();
    }

    /**
     * 初始化view
     */
    private void initView() {
        mCsvShow = (CommolySearchView<BaseSearch>) findViewById(R.id.csv_show);
        mLvShow = (ListView) findViewById(R.id.lv_show);
    }

    /**
     * 初始化搜索
     */
    private void initSearch() {
        mLvShow.setAdapter(mAdapter);
        // 设置数据源
        mCsvShow.setDatas(mCommonSearchBean.getList());
        // 设置适配器
        mCsvShow.setAdapter(mAdapter);
        // 设置搜索
        mCsvShow.setSearchDataListener(new CommolySearchView.SearchDatas<BaseSearch>() {
            @Override
            public List<BaseSearch> filterDatas(List<BaseSearch> datas, List<BaseSearch> filterdatas, String inputstr) {
                for (int i = 0; i < datas.size(); i++) {
                    // 筛选条件,如果有必要,在此做修改
                    if (datas.get(i).getSearchCondition().contains(inputstr)) {
                        filterdatas.add(datas.get(i));
                    }
                }
                return filterdatas;
            }
        });
    }
}

注释写的比较全,每次添加新的搜索类型时,只需要在initAdapter()方法中添加新的搜索类型的扩展即可(主要是实例化新的适配器),有必要的话,可以去修改筛选逻辑或者扩展BaseSearch,大多数包含情况下是不需要修改的.当然本文只是提供一个思路,应该有更好的封装方法.

通用搜索栏CommolySearchView

package com.example.junweiliu.commonlysearchview.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.example.junweiliu.commonlysearchview.R;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by junweiliu on 16/5/31.
 */
public class CommolySearchView<T> extends LinearLayout {
    /**
     * 上下文
     */
    private Context mContext;
    /**
     * 编辑框
     */
    private EditText mEditText;
    /**
     * 清除按钮
     */
    private ImageView mClearImg;
    /**
     * 搜索图标
     */
    private ImageView mSearchBarImg;
    /**
     * 适配器
     */
    private BaseAdapter mAdapter;
    /**
     * 数据源
     */
    private List<T> mDatas = new ArrayList<T>();
    /**
     * 数据源副本
     */
    private List<T> mDupDatas = new ArrayList<T>();

    /**
     * 筛选后的数据源
     */
    private List<T> mFilterDatas = new ArrayList<T>();
    /**
     * 筛选后的数据源副本
     */
    private List<T> mDupFilterDatas = new ArrayList<T>();
    /**
     * 搜索图标
     */
    private Bitmap mSearchIcon;
    /**
     * 搜索框距离左边边距
     */
    private int mSearchIconMarginLeft;
    /**
     * 搜索框距离右边边距
     */
    private int mSearchIconMarginRight;
    /**
     * 清除图标
     */
    private Bitmap mClearIcon;
    /**
     * 清除图标距离左边边距
     */
    private int mClearIconMarginLeft;
    /**
     * 清除图标距离右边边距
     */
    private int mClearIconMarginRight;
    /**
     * 搜索文字大小
     */
    private int mSearchTextSize;
    /**
     * 搜索文字颜色
     */
    private int mSearchTextColor;

    /**
     * 回调接口
     *
     * @param <T>
     */
    public interface SearchDatas<T> {
        /**
         * 参数一:全部数据,参数二:筛选后的数据,参数三:输入的内容
         *
         * @param datas
         * @param filterdatas
         * @param inputstr
         * @return
         */
        List<T> filterDatas(List<T> datas, List<T> filterdatas, String inputstr);
    }

    /**
     * 回调
     */
    private SearchDatas<T> mListener;

    /**
     * 设置回调
     *
     * @param listener
     */
    public void setSearchDataListener(SearchDatas<T> listener) {
        mListener = listener;

    }

    public CommolySearchView(Context context) {
        this(context, null);
    }

    public CommolySearchView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CommolySearchView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        // 自定义属性
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CommolySearchView);
        Drawable searchD = ta.getDrawable(R.styleable.CommolySearchView_SearchBarIconSrc);
        mSearchIcon = drawableToBitamp(searchD);
        mSearchIconMarginLeft = px2dip(context, ta.getDimensionPixelOffset(R.styleable.CommolySearchView_SearchBarIconMarginLeft, 0));
        mSearchIconMarginRight = px2dip(context, ta.getDimensionPixelOffset(R.styleable.CommolySearchView_SearchBarIconMarginRight, 0));
        Drawable clearD = ta.getDrawable(R.styleable.CommolySearchView_ClearIconSrc);
        mClearIcon = drawableToBitamp(clearD);
        mClearIconMarginLeft = px2dip(context, ta.getDimensionPixelOffset(R.styleable.CommolySearchView_ClearIconMarginLeft, 0));
        mClearIconMarginRight = px2dip(context, ta.getDimensionPixelOffset(R.styleable.CommolySearchView_ClearIconMarginRight, 0));
        mSearchTextSize = px2sp(context, ta.getDimensionPixelOffset(R.styleable.CommolySearchView_SearchTextSize, 0));
        mSearchTextColor = ta.getColor(R.styleable.CommolySearchView_SearchTextColor, 0);
        ta.recycle();
        // 绑定布局文件
        LayoutInflater.from(context).inflate(R.layout.searchview_layout, this);
        initView();
    }

    /**
     * 初始化控件
     */
    private void initView() {
        mEditText = (EditText) findViewById(R.id.et_search);
        mClearImg = (ImageView) findViewById(R.id.iv_search_clear);
        mSearchBarImg = (ImageView) findViewById(R.id.iv_search_icon);
        // 处理自定义属性
        if (0 != mSearchIconMarginLeft || 0 != mSearchIconMarginRight) {
            mSearchBarImg.setPadding(mSearchIconMarginLeft, 0, mSearchIconMarginRight, 0);
        }
        if (0 != mClearIconMarginLeft || 0 != mClearIconMarginRight) {
            mClearImg.setPadding(mClearIconMarginLeft, 0, mClearIconMarginRight, 0);
        }
        if (null != mSearchIcon) {
            mSearchBarImg.setImageBitmap(mSearchIcon);
        }
        if (null != mClearIcon) {
            mClearImg.setImageBitmap(mClearIcon);
        }
        if (0 != mSearchTextSize) {
            mEditText.setTextSize(mSearchTextSize);
        }
        if (0 != mSearchTextColor) {
            mEditText.setTextColor(mSearchTextColor);
        }
        // 清空按钮处理事件
        mClearImg.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                mEditText.setText("");
                mClearImg.setVisibility(View.GONE);
                if (null != mDatas) {
                    mDatas.clear();
                }
                mDatas.addAll(mDupDatas);
                mAdapter.notifyDataSetChanged();
                reSetDatas();
            }
        });
        // 搜索栏处理事件
        mEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                // 获取筛选后的数据
                mFilterDatas = mListener.filterDatas(mDupDatas, mFilterDatas, charSequence.toString());
                if (charSequence.toString().length() > 0 && !charSequence.toString().equals("")) {
                    mClearImg.setVisibility(View.VISIBLE);
                } else {
                    mClearImg.setVisibility(View.GONE);
                }
                if (null != mDatas) {
                    mDatas.clear();
                }
                mDatas.addAll(mFilterDatas);
                mAdapter.notifyDataSetChanged();
                reSetDatas();
            }

            @Override
            public void afterTextChanged(Editable editable) {

            }
        });
    }

    /**
     * 获取筛选后的数据
     *
     * @return
     */
    public List<T> getFilterDatas() {
        return (null != mDupFilterDatas && mDupFilterDatas.size() > 0) ? mDupFilterDatas : mDupDatas;
    }


    /**
     * 重置数据
     */
    private void reSetDatas() {
        if (null != mFilterDatas) {
            if (null != mDupFilterDatas) {
                mDupFilterDatas.clear();
                mDupFilterDatas.addAll(mFilterDatas);
            }
            mFilterDatas.clear();
        }
    }


    /**
     * 设置数据源
     *
     * @param datas
     */
    public void setDatas(List<T> datas) {
        if (null == datas) {
            return;
        }
        if (null != mDatas) {
            mDatas.clear();
        }
        if (null != mDupDatas) {
            mDupDatas.clear();
        }
        mDatas = datas;
        mDupDatas.addAll(mDatas);
    }

    /**
     * 设置适配器
     *
     * @param adapter
     */
    public void setAdapter(BaseAdapter adapter) {
        if (null == adapter) {
            return;
        }
        mAdapter = adapter;
    }

    /**
     * drawable转bitmap
     *
     * @param drawable
     * @return
     */
    private Bitmap drawableToBitamp(Drawable drawable) {
        if (null == drawable) {
            return null;
        }
        if (drawable instanceof BitmapDrawable) {
            BitmapDrawable bd = (BitmapDrawable) drawable;
            return bd.getBitmap();
        }
        int w = drawable.getIntrinsicWidth();
        int h = drawable.getIntrinsicHeight();
        Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, w, h);
        drawable.draw(canvas);
        return bitmap;
    }

    /**
     * 将px值转换为dip或dp值,保证尺寸大小不变
     *
     * @param pxValue
     * @return
     * @param(DisplayMetrics类中属性density)
     */
    public int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }


    /**
     * 将px值转换为sp值,保证文字大小不变
     *
     * @param pxValue
     * @return
     */
    public static int px2sp(Context context, float pxValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }
}