通过BaseHolder的方式展示数据

关于BaseHolder的介绍,大家可以看这篇文章对BaseAdapter和ViewHolder的封装


想必大家一定会问,BaseAdapter和ViewHolder不是用来实现对ListView列表展示数据的优化吗,跟今天要说的展示数据有什么关系?

没错,BaseAdapter确实和今天要说的内容没有什么关系,但是ViewHolder就有关系了,这个东西不但可以用来优化ListView的数据展示,还可以用来展示其他任何你想要的数据和布局.


为什么说ViewHolder可以用来展示任何数据和布局呢?使用这种方式有什么优点呢?

我这里所说的ViewHolder是只已经封装过的BaseHolder,它是一个抽象类,该类定义了2个抽象方法:

1. public abstract View initView();

由子类实现,初始化布局

2.public abstract void refreshView(T t);

由子类实现,将数据填充在布局上.


但凡操作界面,无非就这2步骤,初始化布局,将数据填充在布局上,竟然如此,我们何不把这些常规的操作通过面向对象的方式交个一个类去处理,我们只需要把数据塞给它,交由它把数据填充到布局上,最后再把已经填充好数据的布局返回给我们,我们直接拿去展示就好了,这就好比似购物一样,一手交钱一手交货.这样一来,我们的Activity和Fragment上面的代码就会减少很多.也方便我们维护和查阅.

上面说到的这个类,也就是这里所介绍的BaseHolder,在使用的过程中,需要新建它的子类,实现其2个抽象方法,然后在Activity或者Fragment上要用的时候,就只需要4个步骤:

1.创建BaseHolder子类的实例;

2.通过改实例调用BaseHolder的公共方法setData(T t) 将数据传递给BaseHolder;

3.通过改实例调用BaseHolder的公共方法getConvertView()获取已经填充好数据的布局.

4.拿到布局后就可以执行展示布局的逻辑了.


下面分别介绍上面前3个步骤的都做了那些细节:

1.创建BaseHolder子类的实例,这一步是为了执行该实例的父类构造方法,也即是BaseHolder的构造方法,因为在这个基类的构造方法中,会调用initView()抽象方法,该方法被调用,所有实现它的子类的initView()方法都会执行.这个时候相当于布局初始化了.

2.调用setData(T t)公共 方法,该方法一旦执行,数据就会由Activity/Fragment中传递进来并持有.并且,setData(T t)方法还会调用refreshView()抽象方法,那么所有实现它的子类的refreshView(T t)方法都会执行,这个时候相当于填充数据到界面.同时由于setData(T t)方法接收的数据类型是一个泛型,那么在调用的时候就可以确定数据的类型了.

3.调用getConvertView()公共方法,该方法返回的View是布局的根View,这个根View也就是initView()抽象方法的返回值.

而根View里面的所有子View的数据填充,经过步骤2就已经完成了.所以在步骤3的时候拿到的根View是可以直接展示在界面上的了.


好了,分析了原理,我们就来看看代码如何操作.今天就用BaseHolder来实现下面的焦点图效果:


这个焦点图很常见,通常展示在ListView的头部,或者嵌套在ScrollView的内部等等.

代码如下:

MainActivity

public class MainActivity extends AppCompatActivity {
    private List<String> mData;//ListView列表的数据
    private List<String> mHeaderData;//焦点图的url集合

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //初始化数据
        initData();
        //创建ListView
        ListView listView = new ListView(this);
        //创建处理头部ViewPager的BaseHolder子类
        HomeHeaderHolder headerHolder = new HomeHeaderHolder();
        //设置头部数据
        headerHolder.setData(mHeaderData);
        //将填充好头部数据的布局添加到ListView的头部
        listView.addHeaderView(headerHolder.getConvertView());
        //设置适配器
        listView.setAdapter(new HomeAdapter(mData));
        setContentView(listView);
    }
    /**
     * 初始化数据
     */
    private void initData() {
        mData = new ArrayList<>();
        mHeaderData = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            mData.add("测试数据:" + i);
        }
        for (int i = 1; i <= 8; i++) {
            mHeaderData.add("http://127.0.0.1:8090/image?name=" + "image/home0" + i + ".jpg");
        }
    }
    /**
     * 自定义ListView适配器
     */
    private class HomeAdapter extends MyBaseAdapter<String> {

        public HomeAdapter(List<String> data) {
            super(data);
        }
        @Override
        public BaseHolder getBaseHolder(int position) {
            return new BaseHolder<String>() {
                private TextView tvInfo;

                @Override
                public void refreshView(String s) {
                    tvInfo.setText(s);
                }

                @Override
                public View initView() {
                    tvInfo = new TextView(MainActivity.this);
                    tvInfo.setPadding(10, 10, 10, 10);
                    tvInfo.setGravity(Gravity.CENTER_VERTICAL);
                    return tvInfo;
                }
            };
        }
    }
}

上面的代码加上注释和空行总共就63行代码,如果再将ListView的Adapter的代码提取到其他包的话,Activity的代码就更少了.同时,我们也可以看到ListView的定义Adapter的代码也很少,关于这部分的优化可以看这篇文章对BaseAdapter和ViewHolder的封装,这里主要介绍BaseHolder的扩展使用.

我们都知道如果把实现焦点图的代码逻辑统统都写在Activity的话,那么Activity的代码将会变得很庞大,这样非常不利于我们维护和阅读.

接着HomeHeaderHolder这个类

/**
 * Created by mChenys on 2015/11/22.
 */
public class HomeHeaderHolder extends BaseHolder<List<String>> {
    private ViewPager mViewPager;
    private BitmapUtils mBitmapUtils;//XUtils的图片加载库
    private LinearLayout mIndicatorLl;//ViewPager的指示点的根布局
    private int mPreviousPos;// 上一个指示点位置
    private AutoPlayRunnable mAutoPlayRunnable;//ViewPager自动轮播焦点图的Runnable对象
    private Handler mHandler;//处理自动mAutoPlayRunnable的Handler

    public HomeHeaderHolder() {
        super();
        mHandler = new Handler();
        mBitmapUtils = new BitmapUtils(ContextUtils.getContext());
    }

    @Override
    public void refreshView(final List<String> urls) {
        //设置适配器
        mViewPager.setAdapter(new PagerAdapter() {
            @Override
            public int getCount() {
                return Integer.MAX_VALUE;
            }

            @Override
            public boolean isViewFromObject(View view, Object object) {
                return view == object;
            }

            @Override
            public Object instantiateItem(ViewGroup container, int position) {
                position = position % getData().size();
                ImageView imageView = new ImageView(ContextUtils.getContext());
                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);// 设置缩放模式,居中裁剪
                mBitmapUtils.display(imageView, urls.get(position));
                container.addView(imageView);
                return imageView;
            }

            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                container.removeView((View) object);
            }
        });
        //设置ViewPager当前展示的位置
        mViewPager.setCurrentItem(urls.size() * 1000);

        //创建指示器圆点
        if (null != urls && urls.size() > 0) {
            for (int i = 0; i < urls.size(); i++) {
                ImageView imageDot = new ImageView(ContextUtils.getContext());
                LinearLayout.LayoutParams dotLp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
                if (i == 0) {
                    //将第一个点设置为选中状态
                    imageDot.setImageResource(R.drawable.indicator_selected);
                } else {
                    dotLp.leftMargin = 5;//左边距
                    imageDot.setImageResource(R.drawable.indicator_normal);
                }
                mIndicatorLl.addView(imageDot, dotLp);
            }
        }

        //设置ViewPager的滑动监听
        mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }

            @Override
            public void onPageSelected(int position) {
                //当前位置
                position = position % getData().size();
                ImageView currDot = (ImageView) mIndicatorLl.getChildAt(position);
                //将当前显示的位置的圆点变成selected
                currDot.setImageResource(R.drawable.indicator_selected);
                //将上一次被选中的位置重置为normal
                ImageView preDot = (ImageView) mIndicatorLl.getChildAt(mPreviousPos);
                preDot.setImageResource(R.drawable.indicator_normal);
                //更新上一次的位置
                mPreviousPos = position;
            }

            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });

        //实现自动轮播的效果
        mAutoPlayRunnable = new AutoPlayRunnable();
        mAutoPlayRunnable.start();

        //viewPager手指点击的时候需要停止轮播,弹起的时候继续轮播
        mViewPager.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                    case MotionEvent.ACTION_MOVE:
                        mAutoPlayRunnable.stop();
                        break;
                    case MotionEvent.ACTION_UP:
                        mAutoPlayRunnable.start();
                }
                return false;
            }
        });
    }

    @Override
    public View initView() {
        //创建根布局
        RelativeLayout root = new RelativeLayout(ContextUtils.getContext());
        root.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, SizeUtils.getDimen(R.dimen.home_list_header)));
        //创建ViewPager
        mViewPager = new ViewPager(ContextUtils.getContext());
        root.addView(mViewPager, new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
        //创建原点指示器, 在相对布局的右下角在展示
        mIndicatorLl = new LinearLayout(ContextUtils.getContext());
        mIndicatorLl.setOrientation(LinearLayout.HORIZONTAL);
        RelativeLayout.LayoutParams indicatorLp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
        indicatorLp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);// 下方
        indicatorLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);// 右侧
        // 设置内边距
        mIndicatorLl.setPadding(5, 5, 5, 5);
        root.addView(mIndicatorLl, indicatorLp);
        return root;
    }

    /**
     * 轮播图的Runnable
     */
    class AutoPlayRunnable implements Runnable {
        /**
         * 开启轮播
         */
        public void start() {
            //每次启动,先清空所有的消息和runable,避免发送重复的
            mHandler.removeCallbacksAndMessages(null);
            //延时启动
            mHandler.postDelayed(this, 3000);
        }

        /**
         * 暂停轮播
         */
        public void stop() {
            mHandler.removeCallbacksAndMessages(null);
        }

        @Override
        public void run() {
            //获取当前显示的位置
            int currPosition = mViewPager.getCurrentItem();
            //切换到下一张图片
            currPosition++;
            if (currPosition > Integer.MAX_VALUE) {
                currPosition = 0;
            } else if (currPosition < 0) {
                currPosition = Integer.MAX_VALUE;
            }
            mViewPager.setCurrentItem(currPosition);
            //执行下一次循环
            mHandler.postDelayed(this, 3000);
        }
    }
}



上面的代码总共169行,通过这种方式将大大减小了Activity的体积.这就是面向对象的好处.

今天刚好遇上广州最冷的一次寒潮,手指要被冻僵的感觉~~ 


源码下载

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值