Android 实用技巧:用好泛型,少写代码

泛型 安卓开发   2017-01-11 19:43:17 发布
您的评价:
     
0.0
收藏     1收藏
文件夹
标签
(多个标签用逗号分隔)

Android实用技巧之:用好泛型,少写代码

Android开发中,总会遇到大量的繁杂模版代码,重复无味的样板方法,冗长杂余的套路写法,占据了大量的开发时间,并且容易手误出错,极大地降低了编程效率和代码的优雅。

现在,我们通过几个小例子,讲解在TMVP中,如何通过泛型解耦和精简代码,达到高度封装简洁优雅的效果。

1、基类使用泛型限定ViewDataBinding,子类直接指定泛型,一劳永逸:

基类:
public abstract class DataBindingActivity<B extends ViewDataBinding> extends AppCompatActivity {
      public B mViewBinding;

    @Override 
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);  
        View rootView = getLayoutInflater().inflate(this.getLayoutId(), null, false);    
        mViewBinding = DataBindingUtil.bind(rootView);
}

子类:
public class AboutActivity extends DataBindingActivity<ActivityAboutBinding>

2、基类使用泛型限定Presenter泛型,并通过apt工厂初始化Presenter,绑定view,子类直接使用Presenter,不用再new Presenter,一劳永逸:

public abstract class BaseActivity<P extends BasePresenter, B extends ViewDataBinding> extends DataBindingActivity<B> {
    public P mPresenter;

    @Override
    protected void initPresenter() {
        if (this instanceof BaseView &&
                this.getClass().getGenericSuperclass() instanceof ParameterizedType &&
                ((ParameterizedType) (this.getClass().getGenericSuperclass())).getActualTypeArguments().length > 0) {
            Class mPresenterClass = (Class) ((ParameterizedType) (this.getClass()
                    .getGenericSuperclass())).getActualTypeArguments()[0];
            mPresenter = InstanceUtil.getInstance(mPresenterClass);
            mPresenter.setView(this);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter != null) mPresenter.onDetached();
    }
}

3、BaseViewHolder使用ViewDataBinding泛型限定,CoreAdapter使用BaseBean泛型限定,从此告别Adapter,ViewHolder,一劳永逸:

public class CoreAdapter<M extends BaseBean> extends RecyclerView.Adapter<BaseViewHolder> {
    private TypeSelector<M> mTypeSelector;//多个viewtype的选择器
    private List<M> mItemList = new ArrayList<>();

    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new BaseViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), viewType, parent, false));
    }

    @Override
    public void onBindViewHolder(BaseViewHolder holder, int position) {
        holder.mViewDataBinding.setVariable(BR.item, getItem(position));
        holder.mViewDataBinding.executePendingBindings();
    }

4、TRecyclerView使用BaseBean泛型限定,从告别OnRefresh,OnLoadMore,一劳永逸:

public class TRecyclerView<M extends BaseBean> extends FrameLayout implements AdapterPresenter.IAdapterView {
    private SwipeRefreshLayout swipeRefresh;
    private RecyclerView recyclerview;
    private CoreAdapter<M> mCommAdapter;
    private AdapterPresenter mCoreAdapterPresenter;

    public void init(Context context, AttributeSet attrs) {
        swipeRefresh.setOnRefreshListener(()->mCoreAdapterPresenter.fetch(););
        recyclerview.addOnScrollListener(new RecyclerView.OnScrollListener() {
            int lastVisibleItem;

            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (recyclerview.getAdapter() != null
                        && newState == RecyclerView.SCROLL_STATE_IDLE
                        && lastVisibleItem + 1 == recyclerview.getAdapter()
                        .getItemCount() && mCommAdapter.isHasMore)
                    mCoreAdapterPresenter.fetch();
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int arg0, int arg1) {
                super.onScrolled(recyclerView, arg0, arg1);
                lastVisibleItem = mLayoutManager.findLastVisibleItemPosition();
            }
        });
    }
   public TRecyclerView<M> setData(List<M> data) {
        mCommAdapter.setBeans(data);
    }

5、TypeSelector使用泛型类型,viewType对应layoutId,轻松实现复杂列表多viewType的选择器,一劳永逸:

public interface TypeSelector<M> {
        int getType(M m);
    }

    TypeSelector<MessageInfo> mTypeSelector = (item) -> TextUtils.equals(item.creater.objectId, C.ADMIN_ID)
            ? R.layout.list_item_comment_admin : R.layout.list_item_comment_user;// AdminID发送的为Admin消息,其他都是普通消息

    @Override
    public void initView() {
        mViewBinding.lvMsg.setFootData(C.getAdminMsg()).setTypeSelector(mTypeSelector);
        mViewBinding.lvMsg.getPresenter()
            .setRepository(ApiFactory::getMessageList)
            .setParam(C.INCLUDE, C.CREATER)
            .setParam(C.UID, SpUtil.getUser().objectId)
            .fetch();
    }

5、Repository使用泛型结果和HashMap包装多个参数,使用apt自动生成的ApiFactory返回不带泛型的Observable,从此列表类型的网络请求交给AdapterPresenter,一劳永逸:

Repository:

public interface Repository {
    Observable<DataArr> getData(HashMap<String, Object> param);

      public class DataArr<T> {
            public ArrayList<T> results;
      }
}

ApiFactory:

    /**
     * @此方法由apt自动生成
     */
    public static Observable getCommentList(HashMap param) {
        return Api.getInstance().service.getCommentList(
                ApiUtil.getInclude(param),
                ApiUtil.getWhere(param),
                ApiUtil.getSkip(param),
                C.PAGE_COUNT)
                .compose(RxSchedulers.io_main());
    }

AdapterPresenter:

public class AdapterPresenter {
    private Repository mRepository;//仓库
    private HashMap<String, Object> param = new HashMap<>();//设置仓库钥匙
    private int begin = 0;
    private final IAdapterView view;

    public interface IAdapterView {
        void setEmpty();

        void setData(DataArr response, int begin);

        void reSetEmpty();
    }

    public AdapterPresenter(IAdapterView mIAdapterViewImpl) {
        this.view = mIAdapterViewImpl;
    }

    public AdapterPresenter setRepository(Repository repository) {
        this.mRepository = repository;
        return this;
    }

    public AdapterPresenter setParam(String key, String value) {
        this.param.put(key, value);
        return this;
    }

    public void setBegin(int begin) {
        this.begin = begin;
    }

    public void fetch() {
        begin++;
        view.reSetEmpty();
        if (mRepository == null) {
            Log.e("mRepository", "null");
            return;
        }
        param.put(C.PAGE, begin);
        mRepository
                .getData(param)
                .subscribe(
                        res -> view.setData(res, begin),
                        e -> view.setEmpty());
    }
}

使用:
以下代码实现了获取用户列表的请求、分页、展示,整个复杂模块就一句话实现:

public class AboutActivity extends DataBindingActivity<ActivityAboutBinding> {

    @Override
    public void initView() {
        mViewBinding.lvUser.getPresenter().setRepository(ApiFactory::getAllUser).fetch();
    }
}

用户列表的itemType也就是其layoutId,通过attr在xml中设置:

  <com.base.adapter.TRecyclerView
            android:id="@+id/lv_user"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:isRefreshable="false"
            app:itemType="@layout/list_item_user"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />

更新日志:

2017/1/8: 使用Apt封装Retrofit生成ApiFactory替换掉所有的Repository,狂删代码

2017/1/7: 使用DataBinding替换掉所有的ButterKnife,狂删代码

2017/1/6: 使用DataBinding替换掉所有的ViewHolder,狂删代码,从此迈向新时代

2016/12/30:使用Apt生成全局路由TRouter,更优雅的页面跳转,支持传递参数和共享view转场动画

2016/12/29:去掉BaseMultiVH新增VHClassSelector支持更完美的多ViewHolder

2016/12/28:使用Apt生成全局的ApiFactory替代所有的Model

2016/12/27:增加了BaseMultiVH扩展支持多类型的ViewHolder

2016/12/26:抽离CoreAdapterPresenter优化TRecyclerView

 

 

阅读目录

    相关文档  — 更多
    相关经验  — 更多
    相关讨论  — 更多