Android卡片新闻页优化实践

最新文章Android ListView Feed新闻页优化实践
通过将大的ListView逻辑单元进行拆分重构,避免Item过大,viewholder重用率不高,造成卡顿、God Object、子视图刷新不流畅等问题。
http://jungledroid.gitcafe.io/feed-listview.html
效果如图

feedlist

以前的项目有展示Feed的需求,服务器端返回列表,有各种类型的Feed, 客户端根据返回的feed类型进行展示。

常用的实现方法
MVP的方式
  1. Model
    定义抽象Card,为每种Card定义一个Model,
  2. Presenter
    定义基类CardPresenter,为每个Card定义一个Presenter,方法binderView(View convertView,ViewGroup parent)主要用于ListView 的getView调用,让数据和View绑定到一起。
  3. View
    ListView,每一个Presenter代表一个item.
    调用步骤:

    ListView(Adapter getView)
    ->CardPresenter(binderView())
    -> View设置

问题:
  • 加载比较慢,listview view 重用效率不高,Card较大时会出现卡顿
  • 很多时候局部卡片要进行刷新不够流畅
  • 某个卡片局部的显示和隐藏操作,会出现卡顿

最近又遇到相似的需求,于是决定使用不同的方式进行优化。google找到这篇博客https://code.facebook.com/posts/879498888759525/fast-rendering-news-feed-on-android/ 总结起来一句话:将逻辑单元拆分成多个视图单元。

实现思路

Model

先定义一个接口Card代表所有的Card类型

    public interface Card {
        String getType();
    }

定义具体的card实体:

    public class ArticleCard implements Card{
        // ...

         public static String getTypeCode(){
            return "article";
        }
        // ...
    }
Presenter

这里由FeedBinder来担任这个角色.
FeedPartDefine代表一个Item视图单位:

public interface FeedPartDefine {
    FeedBinder createBinder(Card card, FeedUpdateListener feedListener);
}

每个不可拆分的FeedPartDefine内部定义了自己的binder,最终效果多个真正的Item,看起来属于一个Card, ListView的分割线我们定义成一个FeedPartDefine以方便控制,下面给出了一个代码:

public class FeedGapSingleDefine implements FeedPartDefine {
    @Override
    public FeedBinder createBinder(Card card, FeedUpdateListener feedUpdateListener) {
        return new FeedGapBinder();
    }

    private static final class FeedGapBinder implements FeedBinder{
        @Override
        public void prepare() {}

        @Override
        public View bind(View convertView, ViewGroup parent) {
            int key = getViewResourceId();
            if(convertView == null){
                convertView = LayoutInflater.from(parent.getContext()).inflate(key,parent,false);
            }
            return convertView;
        }

        @Override
        public void unbind() {}

        @Override
        public int getViewResourceId() {
            return R.layout.feed_gap;
        }

        @Override
        public View getView(int position,View convertView,ViewGroup parent) {
            return mFeedBinders.get(position).bind(convertView, parent);
        }
        @Override
        public int getViewType() {
            return 1;
        }
        // ...
    }
}

parepare方法可以在解析完后做一些如String,span的初始化工作,unbind可以做一些释放和处理。
最终Adapter中getView:

     @Override
    public View getView(int position,View convertView,ViewGroup parent) {
        return mFeedBinders.get(position).bind(convertView, parent);
    }

另外, 常常一些Card需要展开和收起,听人说可以动态改变ListView的getCount大小,试了下效果不好,因为每次查找都需要循环计算需要跳过的Item数目,可以在Binder中持有一个隐藏的Binder,展开或收起去回调Adapter:

    @Override
    public void onChoiceChange(int checkPosition) {
        mIsExpand = !mIsExpand;
        if(mIsExpand){
              mOnPollExpandListener.expandBinder(this, mResultBinder);
        }else{
            mOnPollExpandListener.collapseBinder(mResultBinder);
        }
    }

Adapter需要做的只是移除和添加该Binder:

@Override
public void expandBinder(FeedBinder choiceBinder, FeedBinder resultBinder) {
    mFeedBinders.add(mFeedBinders.indexOf(choiceBinder) + 1, resultBinder);
    notifyDataSetChanged();
}

@Override
public void collapseBinder(FeedBinder resultBinder) {
    int removeIndex = mFeedBinders.indexOf(resultBinder);
    if(removeIndex != -1) {
        mFeedBinders.remove(removeIndex);
        mFeedBinders.get(removeIndex - 1).onRelativeBinderCollapse();
        notifyDataSetChanged();
    }
}

总的来说效果还不错,比前面的方式流畅很多。代码地址https://github.com/jungledroid/feedlist, 如果有别的思路和想法 ,欢迎交流指点,邮箱fengyutubu@foxmail.com

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值