Android公告栏的几种实现方式与优缺点

       前一段时间做一个app 遇到一个需求 公告栏的可变样式与多行布局效果,然后我到网上看了下别人怎么做的发现一般有几种常用的方式

1.最常用的方法,使用viewflipper ,系统的循环滚动控件,用着也相当的简单, 写好进入与离开的动画然后设置也非常简单,但是有几个小的问题

第一更新数据问题,因为它的工作机制导致的它加载的所有控件实际上都已经存在于布局之上只是属性设置为了GONE不可见

而已,

实际上在大量布局的情况下他是相当占内存的.子条目过多时会出现卡顿并且非常占用资源.并且它还有着重影的小毛病需要你去处理.

2.使用的自定义控件,部分开发者会使用画布去实现这个功能,但是这样的效果就定死了布局也就不能自定义了.

3.综合我的需求,与对性能的追求,就有了我现在的实现方式,结合了RecyclerView的设计思路写了一个满足各种布局的垂直滚动控件  BulletinView 

效果图

/**
 * Created by LXY
 * 公告栏
 */

public class BulletinView extends NestedScrollView {
    private boolean isStop = true;//是否停止
    private View view1, view2;  //上下切换的两个view,view2在上面,view1在下面
    private LayoutInflater inflater;
    private BulletinViewadapter adapter;//适配器
    private int viewType = 0;//当前显示的条目下标
    private LinearLayout linearLayout;// 条目容器
    private OnItemClickListener onItemClickListener;//条目点击事件
    private TranslateAnimation mTranslateAnimationStart, mTranslateAnimationOut;//条目切换动画
    private boolean initData=false;//是否初始化

    public boolean isInitData() {
        return initData;
    }

    public BulletinView(Context context) {
        super(context);
    }

    public BulletinView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        inflater = LayoutInflater.from(getContext());
        linearLayout = new LinearLayout(getContext());
    }

    public BulletinView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        linearLayout = new LinearLayout(getContext());
        inflater = LayoutInflater.from(getContext());
    }


    private void refresh() {
        scrollTo(0, 0);//为了效果美观每次切换前先定位到顶部
        linearLayout.removeAllViews();//移除之前添加的条目,节省空间
        linearLayout.invalidate();
        if (adapter != null) {
            if (viewType >= adapter.getItemCount()) {
                viewType = 0;
            }
            if (view1 == null) {//首次加载时直接显示条目一
                addView(linearLayout);
                view1 = adapter.onBindViewHolder(adapter.onCreateViewHolder(inflater, null, viewType), viewType, adapter.getData().get(viewType));
                view1.setLayoutParams(getLayoutParams());
                linearLayout.addView(view1);
            } else {
                if (viewType + 1 >= adapter.getItemCount()) {
                    //获取即将展示的条目
                    view2 = adapter.onBindViewHolder(adapter.onCreateViewHolder(inflater, null, 0), 0, adapter.getData().get(0));
                    view2.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if (onItemClickListener != null)
                                onItemClickListener.onItemClickListener(adapter.getData().get(0), viewType, v);
                        }
                    });
                } else {
                    //获取即将展示的条目
                    view2 = adapter.onBindViewHolder(adapter.onCreateViewHolder(inflater, null, viewType + 1), viewType + 1, adapter.getData().get(viewType + 1));
                    if (onItemClickListener != null)
                        view2.setOnClickListener(new OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                onItemClickListener.onItemClickListener(adapter.getData().get(viewType + 1), viewType + 1, v);
                            }
                        });
                }
                if (view1 != null) {
                    view1.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if (onItemClickListener != null)
                                onItemClickListener.onItemClickListener(adapter.getData().get(viewType), viewType, v);
                        }
                    });
                    //设置条目布局属性免除条目高度不一导致的问题
                    view1.setLayoutParams(getLayoutParams());
                    view2.setLayoutParams(getLayoutParams());
                    //判断是否设置了自定义动画 ,如果没设置则使用上下滚动默认效果
                    if (mTranslateAnimationStart != null) {
                        view2.setAnimation(mTranslateAnimationStart);
                    } else {
                        setAnimate(view1);
                    }
                    if (mTranslateAnimationOut != null) {
                        view1.setAnimation(mTranslateAnimationOut);
                        mTranslateAnimationOut.setAnimationListener(animationListener);//监测动画执行,执行完成后将不可见布局移除
                    } else {
                        setAnimate(view2).setAnimationListener(animationListener);
                    }
                    //将自布局添加到界面上进行显示
                    linearLayout.addView(view2);
                    linearLayout.addView(view1);
                }

            }
        }
    }

    private Animation.AnimationListener animationListener = new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {

        }

        @Override
        public void onAnimationEnd(Animation animation) {
            linearLayout.removeView(view1);
            view1 = view2;//将当前可见布局赋值给view1;表示下次移除的布局
        }

        @Override
        public void onAnimationRepeat(Animation animation) {

        }
    };

    /**
     * 属性动画
     * 平移
     */
    private TranslateAnimation setAnimate(View view) {
//      imageView中凡是有get,set方法的属性,都可以通过属性动画操作
//      创建属性动画对象,并设置移动的方向和偏移量
//      translationX是imageview的平移属性
        TranslateAnimation objectAnimator = new TranslateAnimation(0, 0, -(float) getHeight(), 0);
//      设置移动时间
        objectAnimator.setDuration(500);
//      开始动画
        objectAnimator.setFillAfter(true);
        view.setAnimation(objectAnimator);
        objectAnimator.start();

        return objectAnimator;
    }


    private Handler mHandler = new Handler();

    private Runnable r = new Runnable() {//循环滚动
        @Override
        public void run() {
            //do something
            //每隔1s循环执行run方法
            if (!isStop) {
                viewType++;
                mHandler.postDelayed(this, 2000);
                invalidate();
                refresh();
            } else {
                mHandler.removeCallbacks(r);
            }
        }
    };

    public void setAdapter(BulletinViewadapter adapter) {//设置适配器
        if (adapter != null) {
            linearLayout.setOrientation(LinearLayout.VERTICAL);
            this.adapter = adapter;
            isStop = false;
            if (!initData){
                mHandler.postDelayed(r, 2000);
            }
            initData=true;
        }
    }

    public void onStop() {//停止滚动
        isStop = true;
        mHandler.removeCallbacks(r);
    }

    public void onStart() {//开始滚动
        isStop = false;
        if (adapter != null)
            mHandler.postDelayed(r, 2000);
    }


    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {//条目点击事件
        if (onItemClickListener != null) {
            this.onItemClickListener = onItemClickListener;
        }
    }

    public static abstract class BulletinViewadapter<T> {//适配器抽象类
        private List<T> data;

        public BulletinViewadapter(List<T> data) {
            this.data = data;
        }

        public List<T> getData() {
            return data;
        }

        public  abstract int getItemCount();

        public  abstract View onBindViewHolder(View itemView, int position, T itemData);

        protected abstract View onCreateViewHolder(LayoutInflater inflater, ViewGroup parent, int viewType);
    }

    public interface OnItemClickListener<T> {
        void onItemClickListener(T itemData, int pointer, View view);
    }

    //设置开始动画,可不设置
    public void setmTranslateAnimation(TranslateAnimation mTranslateAnimationStart
            , TranslateAnimation mTranslateAnimationOut) {
        this.mTranslateAnimationStart = mTranslateAnimationStart;
        this.mTranslateAnimationOut = mTranslateAnimationOut;
    }
}

使用起来相信大家都会非常方便

适配器也非常的简单.好了使用这个view的方式就这么简单,并且数据加载也是显示一个加载一个,完全不吃性能.

public class ComplexViewAdapter extends BulletinView.BulletinViewadapter<CrowdListBean.HeadlineBean> {
    public ComplexViewComplexViewAdapter(List<CrowdListBean.HeadlineBean> data) {
        super(data);
    }

    @Override
    public int getItemCount() {
        return getData().size();
    }

    @Override
    public View onBindViewHolder(View itemView, int position, CrowdListBean.HeadlineBean itemData) {
//为布局绑定数据
        ((TextView) itemView.findViewById(R.id.title)).setText(itemData.getTitle());
        ((TextView) itemView.findViewById(R.id.secondTitle)).setText(itemData.getMemo());
        return itemView;
    }

    @Override
    protected View onCreateViewHolder(LayoutInflater inflater, ViewGroup parent, int viewType) {
        //布局可随意绑定,更改,显示时可滚动多种布局,不局限于一种
        return inflater.inflate(R.layout.complex_view, parent);
    }

}
private void initBulletinView(CrowdListBean bean) {//数据源自己填写
        ComplexViewMF marqueeFactory = new ComplexViewMF(bean.getHeadline());
        marqueeView.setAdapter(marqueeFactory);
        marqueeView.setOnItemClickListener(new BulletinView.OnItemClickListener<CrowdListBean.HeadlineBean>() {
            @Override
            public void onItemClickListener(CrowdListBean.HeadlineBean itemData, int pointer, View view) {
                if (itemData!=null){
                    NativeUtils.get(getActivity(),itemData.getUrl(),"");
                }
            }
        });

    }

git demo地址  https://github.com/IHoveYou/bulletinview.git

猜你喜欢

转载自blog.csdn.net/qq_35644925/article/details/81477060