第一次尝试——使用Retrofit+Dagger架构一个拿来就可以使用的Android空项目

42 篇文章 1 订阅
21 篇文章 0 订阅

    刚开始来公司的时候,接手了两个项目,两个项目的架构是用的同一个,虽然做了一些封装,但在我这个菜鸟的眼里,也觉得不好,单其中一个项目在不久后就上线了,因为百分之八十以上的页面在我来之前就写好了,而且之前也上传了几个版本了,在这种情况下,虽然后来的扩展和修改bug让我很头疼,也还是没有想着做一些改变。另外一个项目至今没有上线,还有一些东西没有完成,而另一个开发者又离职了,UI给我列出来一大堆要改的页面和逻辑,在缝缝补补做了半个月的时候,我终于终于没办法忍受,于是毅然决定重构整个项目,哦,不对,是重新做这个项目。

    那么,既然重新做,就不能用之前的架构了,缝缝补补太痛苦,所以开始了这篇博客的主题所指,当然,在我这个阅历下,能想到的封装思路和能够封装的程度都是有限的,只是给初级的开发者一些参考,大神,请绕步。。。

一、网络加载

    首先,选择网络加载框架,恩,之前就一直听说Retrofit,是最近非常流行的一个框架,但是一直没有机会自己来好好玩一次,所以在研究了一段时间之后毅然决定就它了。因为自己研究的程度目前还不能给大家分析,所以,详情请看下后面的代码吧!如果你都还没听说过,就去看看官方文档,这个不是今天的重点。

二、图片加载

    然后就是图片加载框架,现在流行的有三大图片框架:

        1)    Picasso

        2)    Glide

        3)    Fresco

    当然,ImageLoader是历史舞台的大佬,不过我好久没用过了。。。

    先简单的介绍下这三个框架:

    Picasso :和Square的网络库一起能发挥最大作用,因为Picasso可以选择将网络请求的缓存部分交给了okhttp实现。

    Glide:模仿了Picasso的API,而且在他的基础上加了很多的扩展(比如gif等支持),Glide默认的Bitmap格式是RGB_565,比    Picasso默认的ARGB_8888格式的内存开销要小一半;Picasso缓存的是全尺寸的(只缓存一种),而Glide缓存的是跟ImageView尺寸相同的(即56*56和128*128是两个缓存) 。

    FB的图片加载框架Fresco:最大的优势在于5.0以下(最低2.3)的bitmap加载。在5.0以下系统,Fresco将图片放到一个特别的内存区域(Ashmem区)。当然,在图片不显示的时候,占用的内存会自动被释放。这会使得APP更加流畅,减少因图片内存占用而引发的OOM。为什么说是5.0以下,因为在5.0以后系统默认就是存储在Ashmem区了。

    再看下三者的比较:

    Picasso所能实现的功能,Glide都能做,无非是所需的设置不同。但是Picasso体积比起Glide小太多如果项目中网络请求本身用的就是okhttp或者retrofit(本质还是okhttp),那么建议用Picasso,体积会小很多(Square全家桶的干活)。Glide的好处是大型的图片流,比如gif、Video,如果你们是做美拍、爱拍这种视频类应用,建议使用。

    Fresco在5.0以下的内存优化非常好,代价就是体积也非常的大,按体积算Fresco>Glide>Picasso,不过在使用起来也有些不便(小建议:他只能用内置的一个ImageView来实现这些功能,用起来比较麻烦,我们通常是根据Fresco自己改改,直接使用他的Bitmap层)

    因为使用了retrofit,再加上本应用对图片的要求不高,所以我们这里最完美的选择就是Picasso了,那就决定了,就用它。

三、其他

    做完了这两个选择后,接下来要考虑的事情是对基类的封装。当然最基础的基类有BaseApplication,BaseActivity和BaseFragment三个,对于BaseApplication的话,我没有做太多的处理,现在想到一个对网络状态进行监听的方法,然后在当网络由不可用转为确实可用的时候,发送一个消息给所有加载失败的页面,重新加载数据,这个在我写完这篇博客后会加上去的。

    BaseActivity和的ParentActivity封装

    至于ParentActivity是干嘛的?因为我需要在BaseActivity中封装好对错误页面,未加载完成的页面,加载成功后的页面和对标题栏等的一些页面的处理,但是很多页面其实是不需要加载网络的,所以不需要加载错误,未加载完成的页面,他只需要直接显示出来一些界面就好,所以ParentActivity就诞生了,在这个类里面,就是单单的做了除了与View有关的其他所有操作。

    ParentActivity.java

    

 
 
  1. /**
  2. * Created by cretin on 16/10/27.
  3. */
  4. public abstract class ParentActivity extends AppCompatActivity {
  5.    //记录下所有的Activity
  6.    public final static List<ParentActivity> mActivities = new LinkedList<ParentActivity>();
  7.    public static boolean isKitkat;
  8.    public static ParentActivity mActivity;
  9.    @Override
  10.    protected void onCreate(@Nullable Bundle savedInstanceState) {
  11.        super.onCreate(savedInstanceState);
  12.        getSupportActionBar().hide();
  13.        synchronized (mActivities) {
  14.            mActivities.add(this);
  15.        }
  16.        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
  17.            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  18.            isKitkat = true;
  19.        }
  20.        initView(null);
  21.    }
  22.    protected abstract void initView(View view);
  23.    @Override
  24.    protected void onResume() {
  25.        super.onResume();
  26.        mActivity = this;
  27.    }
  28.    @Override
  29.    protected void onPause() {
  30.        super.onPause();
  31.        mActivity = null;
  32.    }
  33.    @Override
  34.    protected void onDestroy() {
  35.        super.onDestroy();
  36.        synchronized (mActivities) {
  37.            mActivities.remove(this);
  38.        }
  39.        ButterKnife.unbind(this);
  40.    }
  41. }

    在上面的代码中,首先定义了一个List<ParentActivity> mActivities的集合,用于存储所有的已打开的Activtiy,便于以后统一的去处理和查询;还定义了一个public static boolean isKitkat;这个是用来记录当前Android版本支不支持沉浸式状态栏,如果支持,直接使用沉浸式状态栏;还有一个抽象方法initView(null);是用来暴露给用户,让他去处理View相关的事情,但是由于ParentActivity没有处理View相关的操作,所以传入了null;在onDestroy方法里面,移除掉了当前的Activity,并且ButterKnife.unbind(this);由于整个架构默认使用ButterKnife来对View进行绑定,所以在退出的时候主动unbind掉,免得每个子类都要各自去处理。

    在BaseActivity里面,就需要处理上面ParentActivity没有能处理的事情,所以BaseActivity是继承自ParentActivity的。

具体如下:

BaseActivity.java

 
 
  1. package com.cretin.www.createnewprojectdemo.base;
  2. import android.graphics.Color;
  3. import android.graphics.drawable.AnimationDrawable;
  4. import android.os.Bundle;
  5. import android.text.TextUtils;
  6. import android.view.View;
  7. import android.widget.ImageView;
  8. import android.widget.RelativeLayout;
  9. import android.widget.TextView;
  10. import com.cretin.www.createnewprojectdemo.R;
  11. import com.cretin.www.createnewprojectdemo.utils.ViewUtils;
  12. import com.cretin.www.createnewprojectdemo.view.CustomProgressDialog;
  13. import butterknife.ButterKnife;
  14. import retrofit2.Call;
  15. import retrofit2.Callback;
  16. import retrofit2.Response;
  17. import rx.Subscription;
  18. import rx.subscriptions.CompositeSubscription;
  19. /**
  20. * Created by cretin on 16/10/27.
  21. * 如果是需要处理自己的逻辑 则继承这个
  22. * 如果只是给加载Fragment提供一个容器 则继承ParentActivity
  23. */
  24. public abstract class BaseActivity extends ParentActivity {
  25.    private CustomProgressDialog dialog;
  26.    private OnTitleAreaCliclkListener onTitleAreaCliclkListener;
  27.    private TextView tvMainTitle;
  28.    private ImageView ivMainBack;
  29.    private ImageView ivMainRight;
  30.    private TextView tvMainRight;
  31.    private RelativeLayout relaLoadContainer;
  32.    private TextView tvLoadingMsg;
  33.    private ImageView ivBack;
  34.    private CompositeSubscription mCompositeSubscription;
  35.    protected void addSubscription(Subscription s) {
  36.        if ( this.mCompositeSubscription == null ) {
  37.            this.mCompositeSubscription = new CompositeSubscription();
  38.        }
  39.        this.mCompositeSubscription.add(s);
  40.    }
  41.    @Override
  42.    protected void onCreate(Bundle savedInstanceState) {
  43.        super.onCreate(savedInstanceState);
  44.        View view = getLayoutInflater().inflate(R.layout.layout_base_activity, null);
  45.        setContentView(view);
  46.        initHeadView(view);
  47.        initContentView(view);
  48.        if ( isKitkat ) {
  49.            view.findViewById(R.id.ll_main_title).setPadding(0, ViewUtils.getStatusBarHeights(), 0, 0);
  50.        }
  51.        initData();
  52.        initEvent();
  53.    }
  54.    private AnimationDrawable animationDrawable;
  55.    private void initContentView(View view) {
  56.        RelativeLayout container = ( RelativeLayout ) view.findViewById(R.id.main_container);
  57.        relaLoadContainer = ( RelativeLayout ) view.findViewById(R.id.load_container);
  58.        tvLoadingMsg = ( TextView ) view.findViewById(R.id.loading_msg);
  59.        ImageView imageView = ( ImageView ) view
  60.                .findViewById(R.id.loading_image);
  61.        animationDrawable = ( AnimationDrawable ) imageView
  62.                .getBackground();
  63.        animationDrawable.start();
  64.        View v = getLayoutInflater().inflate(getContentViewId(), null);
  65.        ButterKnife.bind(this, v);
  66.        container.addView(v);
  67.        initView(v);
  68.    }
  69.    //onResponse子类去实现
  70.    public abstract class ResultCall<T> implements Callback<T> {
  71.        @Override
  72.        public void onResponse(Call<T> call, Response<T> response) {
  73.            hidProgressView();
  74.            onResponse(response);
  75.        }
  76.        protected abstract void onResponse(Response<T> response);
  77.        @Override
  78.        public void onFailure(Call<T> call, Throwable t) {
  79.            showErrorView();
  80.            onError(call, t);
  81.        }
  82.        protected abstract void onError(Call<T> call, Throwable t);
  83.    }
  84.    //隐藏正在加载视图
  85.    public void hidProgressView() {
  86.        if ( relaLoadContainer != null )
  87.            relaLoadContainer.setVisibility(View.GONE);
  88.        if ( animationDrawable != null )
  89.            animationDrawable.stop();
  90.    }
  91.    //显示正在加载视图
  92.    public void showProgressView() {
  93.        if ( relaLoadContainer != null && relaLoadContainer.getVisibility() == View.GONE )
  94.            relaLoadContainer.setVisibility(View.VISIBLE);
  95.        if ( animationDrawable != null )
  96.            animationDrawable.start();
  97.    }
  98.    //隐藏返回按钮
  99.    public void hidBackIv() {
  100.        if ( ivBack != null && ivBack.getVisibility() == View.VISIBLE )
  101.            ivBack.setVisibility(View.GONE);
  102.    }
  103.    //显示加载错误
  104.    public void showErrorView() {
  105.        if ( relaLoadContainer != null && relaLoadContainer.getVisibility() == View.GONE )
  106.            relaLoadContainer.setVisibility(View.VISIBLE);
  107.        if ( animationDrawable != null )
  108.            animationDrawable.stop();
  109.        tvLoadingMsg.setText("加载错误");
  110.    }
  111.    //初始化头部视图
  112.    private void initHeadView(View view) {
  113.        tvMainTitle = ( TextView ) view.findViewById(R.id.tv_title_info);
  114.        ivMainBack = ( ImageView ) view.findViewById(R.id.iv_back);
  115.        ivMainRight = ( ImageView ) view.findViewById(R.id.iv_right);
  116.        tvMainRight = ( TextView ) view.findViewById(R.id.tv_right);
  117.        ivBack = ( ImageView ) view.findViewById(R.id.iv_back);
  118.        ivMainBack.setOnClickListener(new View.OnClickListener() {
  119.            @Override
  120.            public void onClick(View v) {
  121.                finish();
  122.                if ( onTitleAreaCliclkListener != null )
  123.                    onTitleAreaCliclkListener.onTitleAreaClickListener(v);
  124.            }
  125.        });
  126.        ivMainRight.setOnClickListener(new View.OnClickListener() {
  127.            @Override
  128.            public void onClick(View v) {
  129.                if ( onTitleAreaCliclkListener != null )
  130.                    onTitleAreaCliclkListener.onTitleAreaClickListener(v);
  131.            }
  132.        });
  133.        tvMainRight.setOnClickListener(new View.OnClickListener() {
  134.            @Override
  135.            public void onClick(View v) {
  136.                if ( onTitleAreaCliclkListener != null )
  137.                    onTitleAreaCliclkListener.onTitleAreaClickListener(v);
  138.            }
  139.        });
  140.    }
  141.    /**
  142.     * 显示加载对话框
  143.     *
  144.     * @param msg
  145.     */
  146.    public void showDialog(String msg) {
  147.        if ( dialog == null ) {
  148.            dialog = CustomProgressDialog.createDialog(this);
  149.            if ( msg != null && !msg.equals("") ) {
  150.                dialog.setMessage(msg);
  151.            }
  152.        }
  153.        dialog.show();
  154.    }
  155.    /**
  156.     * 关闭对话框
  157.     */
  158.    public void stopDialog() {
  159.        if ( dialog != null && dialog.isShowing() ) {
  160.            dialog.dismiss();
  161.        }
  162.    }
  163.    public void setOnTitleAreaCliclkListener(OnTitleAreaCliclkListener onTitleAreaCliclkListener) {
  164.        this.onTitleAreaCliclkListener = onTitleAreaCliclkListener;
  165.    }
  166.    //设置Title
  167.    protected void setMainTitle(String title) {
  168.        if ( !TextUtils.isEmpty(title) )
  169.            tvMainTitle.setText(title);
  170.    }
  171.    //设置TitleColor
  172.    protected void setMainTitleColor(String titleColor) {
  173.        if ( !TextUtils.isEmpty(titleColor) )
  174.            setMainTitleColor(Color.parseColor(titleColor));
  175.    }
  176.    //设置TitleColor
  177.    protected void setMainTitleColor(int titleColor) {
  178.        tvMainTitle.setTextColor(titleColor);
  179.    }
  180.    //设置右边TextView颜色
  181.    protected void setMainTitleRightColor(int tvRightColor) {
  182.        tvMainRight.setTextColor(tvRightColor);
  183.    }
  184.    //设置右边TextView颜色
  185.    protected void setMainTitleRightColor(String tvRightColor) {
  186.        if ( !TextUtils.isEmpty(tvRightColor) )
  187.            setMainTitleRightColor(Color.parseColor(tvRightColor));
  188.    }
  189.    //设置右边TextView大小
  190.    protected void setMainTitleRightSize(int size) {
  191.        tvMainRight.setTextSize(size);
  192.    }
  193.    //设置右边TextView内容
  194.    protected void setMainTitleRightContent(String content) {
  195.        if ( !TextUtils.isEmpty(content) ) {
  196.            if ( tvMainRight.getVisibility() == View.GONE )
  197.                tvMainRight.setVisibility(View.VISIBLE);
  198.            tvMainRight.setText(content);
  199.        }
  200.    }
  201.    //设置左边ImageView资源
  202.    protected void setMainLeftIvRes(int res) {
  203.        if ( ivMainBack.getVisibility() == View.GONE )
  204.            ivMainBack.setVisibility(View.VISIBLE);
  205.        ivMainBack.setImageResource(res);
  206.    }
  207.    //设置又边ImageView资源
  208.    protected void setMainRightIvRes(int res) {
  209.        if ( ivMainRight.getVisibility() == View.GONE )
  210.            ivMainRight.setVisibility(View.VISIBLE);
  211.        ivMainRight.setImageResource(res);
  212.    }
  213.    interface OnTitleAreaCliclkListener {
  214.        void onTitleAreaClickListener(View view);
  215.    }
  216.    protected abstract void initData();
  217.    protected abstract int getContentViewId();
  218.    protected void initEvent() {
  219.    }
  220.    @Override
  221.    protected void onDestroy() {
  222.        super.onDestroy();
  223.        ButterKnife.unbind(this);
  224.        if ( this.mCompositeSubscription != null ) {
  225.            this.mCompositeSubscription.unsubscribe();
  226.        }
  227.    }
  228. }


layouy_base_activity.xml:

 
 
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.    xmlns:tools="http://schemas.android.com/tools"
  4.    android:layout_width="match_parent"
  5.    android:layout_height="match_parent"
  6.    android:orientation="vertical">
  7.    <LinearLayout
  8.        android:id="@+id/ll_main_title"
  9.        android:layout_width="match_parent"
  10.        android:layout_height="wrap_content"
  11.        android:background="@color/white"
  12.        android:orientation="vertical">
  13.        <RelativeLayout
  14.            android:layout_width="match_parent"
  15.            android:layout_height="wrap_content">
  16.            <ImageView
  17.                android:id="@+id/iv_back"
  18.                android:layout_width="wrap_content"
  19.                android:layout_height="wrap_content"
  20.                android:layout_centerVertical="true"
  21.                android:layout_marginLeft="5dp"
  22.                android:clickable="true"
  23.                android:src="@mipmap/arrow_left"/>
  24.            <TextView
  25.                android:id="@+id/tv_title_info"
  26.                android:layout_width="wrap_content"
  27.                android:layout_height="wrap_content"
  28.                android:layout_centerInParent="true"
  29.                android:padding="13dp"
  30.                android:textColor="@color/font_black3"
  31.                android:textSize="@dimen/text_size_17"
  32.                tools:text="标题" />
  33.            <TextView
  34.                android:id="@+id/tv_right"
  35.                android:layout_width="wrap_content"
  36.                android:layout_height="wrap_content"
  37.                android:layout_alignParentRight="true"
  38.                android:layout_centerInParent="true"
  39.                android:layout_marginRight="5dp"
  40.                android:clickable="true"
  41.                android:drawablePadding="5dp"
  42.                android:textColor="@color/font_black3"
  43.                android:textSize="@dimen/text_size_17"
  44.                android:visibility="gone"
  45.                tools:text="修改" />
  46.            <ImageView
  47.                android:id="@+id/iv_right"
  48.                android:layout_width="wrap_content"
  49.                android:layout_height="wrap_content"
  50.                android:layout_alignParentRight="true"
  51.                android:layout_centerVertical="true"
  52.                android:layout_marginRight="5dp"
  53.                android:visibility="gone"
  54.                tools:src="@mipmap/ic_launcher" />
  55.        </RelativeLayout>
  56.        <include layout="@layout/line" />
  57.    </LinearLayout>
  58.    <RelativeLayout
  59.        android:id="@+id/main_container"
  60.        android:layout_width="match_parent"
  61.        android:layout_height="match_parent"
  62.        android:layout_below="@+id/ll_main_title" />
  63.    <RelativeLayout
  64.        android:id="@+id/load_container"
  65.        android:layout_width="match_parent"
  66.        android:layout_height="match_parent"
  67.        android:layout_below="@+id/ll_main_title"
  68.        android:background="@color/white">
  69.        <LinearLayout
  70.            android:layout_width="120dip"
  71.            android:layout_height="120dip"
  72.            android:layout_centerInParent="true"
  73.            android:gravity="center"
  74.            android:orientation="vertical">
  75.            <ImageView
  76.                android:id="@+id/loading_image"
  77.                android:layout_width="wrap_content"
  78.                android:layout_height="wrap_content"
  79.                android:layout_gravity="center_vertical|center_horizontal"
  80.                android:background="@drawable/load_progress_bar" />
  81.            <TextView
  82.                android:id="@+id/loading_msg"
  83.                android:layout_width="wrap_content"
  84.                android:layout_height="wrap_content"
  85.                android:layout_gravity="center_vertical|center_horizontal"
  86.                android:padding="10dip"
  87.                android:text="正在加载..."
  88.                android:textSize="12sp" />
  89.        </LinearLayout>
  90.    </RelativeLayout>
  91. </RelativeLayout>

     先看上面的布局文件,分为两个大的部分,一个是标题栏,一个是内容区域,标题栏左边有一个默认的返回ImageView,中间是一个TextView,用于显示标题,右边有一个TextView和一个ImageView,TextView用来显示比如修改的操作,ImageView用来显示设置什么的图标,默认左边的返回按钮个标题栏可见状态,右边两个都是隐藏的,因为用到他们的机会还是挺少的。在内用区域,有一个显示内容的布局,到时候用来显示需要加载的布局,还有一个正在加载的布局,用来显示正在加载中。。这个可自己去根据需求定制,这个页面也可以作为加载失败的页面,根据需求来就可以,当这个界面的网络加载完成之后,就可以主动的隐藏掉这个正在加载的页面就可以了,用户体验相对也会好一点。

    然后来看下BaseActivity中的操作,首先,我们避免写findViewById的操作,所以我们在BaseActivity中封装好了对ButterKnife的bind和unbind操作,这样在子类中就只需要简单的操作就行,不需要再bind和unbind了,然后提供了一系列的对标题栏自定义的操作,比如设置标题内容,文字大小颜色,设置标题栏右边的图标的资源。。等等,这些根据需求,不够的还可以自己再自行添加就好;还有在前面我们判断了Android版本是否支持沉浸式状态栏,如果支持的话,我在BaseActivity中也对标题栏进行了setPadding的操作,因为不设置的话,标题会跟状态栏的文字重叠,就会比较难看,这种操作不需要子类来理会,所以封装出来会比较好;另外我还提供了一个比较方便的的方法,就是showDialog和stopDialog的操作,当页面需要二次请求网路的时候,显示showDialog,加载完就stopDialog即可,这样用户体验就会好一点;还有一个很大的点就是对网络请求的封装,这个因为我现在对Retrofit还不是非常熟练,所以只进行了简单的封装,到时候经过再研究之后,再来进行完善!在目前的代码中,我写了一个抽象方法,在抽象方法中对请求的响应做出一个初级的判断,如果请求是成功的,并且数据的返回也是正常的,那么回调给调用者,否则直接显示网络加载错误的页面,统一的对网络错误进行处理,不过,这个要能真正完美的使用,需要后台不要太差。。不然老是返回乱七八糟的错误给你,就不太好统一处理了。。到此,BaseActivity的处理也要到此为止了。

    BaseFragment的封装

    对于BaseFragment来说,其封装跟BaseActtivity是一模一样的,本质的差别也就是一个是Activity一个是Fragment,所以就不再赘述了。

    BaseFragmentActivity和BackFragmentActivity的封装

    为什么会有这两个类的存在?我现在比较喜欢的一种加载界面方式是使用Fragment碎片化的方式,就是开一个Activity,这个Activity就是承载Fragment的一个容器,所以这个Activity啥都不做,就只作用于添加Fragment,显然,这个Activity是不需要加载网络和一些其他操作的,所以我把他们俩都是集成自ParentActivity,这个具体看下项目中的操作。看这两个类的名字也很清楚,继承BaseFragmentActivity的时候,代表要加载的Fragment是不需要假如回退栈的,继承BackFragmentActivity的时候,代表要加载的Fragment是需要假如回退栈的,当然,在计入Fragment的时候,也可以使用动画,具体的使用,看Demo就好,在此不再赘述了,代码什么的还是要自己看,理解才快。


工具类

    最后再来说下项目中默认添加使用的一些其他好用的东西:

    本地数据的存储:Hawk 

    这个工具是当年一个叫Calvin的大哥告诉我的,一用起来就爱不释手。

    Hawk 是一个非常便捷的数据库  . 操作数据库只需一行代码 , 能存任何数据类型 .

    github 地址: https://github.com/orhanobut/hawk

    Hawk 是一个简单的  key-value  数据库

    它使用: 

    AES 加密

    能选择使用SharedPreferences  或者  SQLite

       Gson解析

    提供:

    安全数据持久化

    能存储任何类型

    具体的使用,大家去Github上去看吧,相信只要你用过你就会喜欢上他的,信我。

    Android事件总线:EventBus

    这个不多说,已经非常成熟了。简单介绍下

    实际项目开发过程中,经常遇到如下场景:不同的应用程序组件的控件间具有一定的相互关联性,其中用户对后者进行的某种操作会引起前者的相应改变。举一个具体的场景:以糗事百科为例,在糗事列表页和详情页页,对于每个糗事而言,布局基本一致,在详情页点击了个赞,赞的数量增加,同时赞的图标发生了变化,此时返回到列表页,此糗事上的赞图标以及数量与刚刚详情页的需要保持一致。在举一个例子,对于多个底部导航tab下的资讯类阅读app,在咨询详情页点击了收藏,然后收藏成功,此时回到底部tab中的个人中心,假如个人中心中有我的收藏,同时后面显示的是收藏数量,此时此收藏数量需要同于于刚刚用户所进行的收藏/取消收藏而即时更改数字。凡此种种,类似需求场景非常常见。

    有时候,当此类需求相对简单时,通过接口以实现回调等方式可以完成,但是当不同组件/控件之间的关系纷繁复杂时,基于接口的方案不仅使得代码非常繁琐,同时是的程序逻辑很混乱,基于此,EventBus,为此类需求的实现提供了非常方便的方案。

    butterknife注解框架的偷懒插件

    这个也不多说,已经非常成熟了。简单介绍下

    事实上这是个Android Studio的plugin,他可以让你在添加Butterkinfe注解时偷偷懒,直接点击几下鼠标既可以完成注解的增加,同时还是图形化的操作,可以说,大大的减轻了开发负担。尤其是当你的layout中有很多很多的view需要通过findviewbyid来获得引用时。实际上如果不用这个插件而通过手打加ButtefKnife注解的方式,要是view很多启示也挺麻烦的,不是吗?


    到此这篇博客也就到了尾声了,可能有很多地方还不是很准确,希望大家多提提意见,而且,这个封装也还很基础,希望大佬有时间看下,多提提意见和建议,再次谢过了。如果觉得不是太烂,记得Star一下哦。

    

    我是Cretin,一个可爱的小男孩。

    下面是Github的地址:

    https://github.com/MZCretin/CreateNewProjectDemo


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值