Android-多列表的项目(Rxjava+Rtrofit+Recyclerview+Glide+Adapter封装)之(一)项目架构

好久没写博客了,主要是之前要奋战完成一个外包项目,也因为是第一次外包,很多东西要处理,当然也学到很多东西,在这个系列,我会把这次外包的一些代码分享给大家,一起学习。

项目介绍:1.这是一个简易使用的网络请求封装项目,可快速移植实现安卓网络层的开发;2.这是一个常见的app模板,使用较好的app架构,并实现一系列的优化。

先上几张图看看

这里写图片描述
这里写图片描述

如例子的两个页面可以看到就是想这样的样式,以及请求,我们在android应用开发中经常遇到。

好了,废话不多说,下面开始正文。


文章结构:(1)整个应用的架构模式;(2)Activity和Fragment的封装;

第二篇在这里:Android-多列表的项目(Rxjava+Rtrofit+Recyclerview+Glide+Adapter封装)之(二)网络层的封装

一、整个应用的架构模式:

这里写图片描述

主要还是MVC模式。不懂MVC的同学请看Android中的MVC模式。对应到我们的项目,M层就是我的bean包了,C层则由activity来担当,V层则由自定义view以及xml文件来担当。contronller(控制器)是一个中间桥梁的作用,通过接口通信来协同 View(视图)和Model(模型)工作,起到了两者之间的通信作用。

二、Activity和Fragment的封装:

这里写图片描述

可以看到,我封装了三层抽象activity,各自负责不同的业务。这个是很常见的app的activity封装。

1.BaseActivity是app设计中的最高父类,则由它处理一些加载的优化,以及使用了模板模式,制定一系列的抽象方法,管理相关的app整体功能(如退出整个app)。

public abstract class BaseActivity extends AppCompatActivity {
   //存储全部activity
    private static List<Activity> activities = new ArrayList<>();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Logger.i("Activity--->" + getClass().getSimpleName());
        addActivity(this);

        if (getLayoutId() != 0) {
            setContentView(getLayoutId());
        } else {
            throw new IllegalArgumentException("返回一个正确的ContentView");
        }
        ButterKnife.bind(this);//ButterKnife必须在这里bing,也就是初始化,那么其余activity就不用顾及这个了

        initView();//模板模式
        initEvent();
        loadData();
    }

    protected abstract int getLayoutId();

    protected abstract void initView();

    protected abstract void initEvent();

    protected abstract void loadData();

    @Override
    protected void onPause() {
        super.onPause();
        removeActivity(this);//释放所有资源
    }


    // 添加Activity到容器中
    private void addActivity(Activity activity) {
        if (activity != null && !activities.contains(activity)) {
            activities.add(activity);
        }
    }

    private void removeActivity(Activity activity) {
        if (activity != null && activities.contains(activity)) {
            activities.remove(activity);
        }
    }

    // 退出整个APP
    public static void exit() {
        if (activities != null && activities.size() > 0) {
            for (Activity activity : activities) {
                activity.finish();
            }
        }
        System.exit(0);
    }
}

2.ToolbarActivity是每个activity需要标题栏封装;主要是为了节省代码,并使得继承此activity的activity得以逻辑更加清晰。

public abstract class ToolbarActivity extends BaseActivity {

    @BindView(R.id.toolbar)
    Toolbar toolbar;
    @BindView(R.id.title)
    public TextView title;



    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        toolbar.setTitle("");//节省代码啦,以后app的activity不用再为标题烦恼
        setSupportActionBar(toolbar);
        if (getSupportActionBar() != null) {
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);//决定左上角的图标是否可以点击
        }
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }

}

3.DrawerActivity是抽屉activity;这样写的话,让MainActivity去继承就会使main的代码更加简洁,并且可以复用代码

public abstract class DrawerActivity extends ToolbarActivity implements NavigationView.OnNavigationItemSelectedListener {

    @BindView(R.id.navigation_view)
    NavigationView navigationView;
    @BindView(R.id.drawer_layout)
    DrawerLayout drawerLayout;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close);
        drawerToggle.syncState();//该方法会自动和actionBar关联, 将开关的图片显示在了action上,如果不设置,也可以有抽屉的效果,不过是默认的图标  
        drawerLayout.setDrawerListener(drawerToggle);

        drawerToggle.setDrawerIndicatorEnabled(false);
        toolbar.setNavigationIcon(R.drawable.toolbar_navigation);//设置那个抽屉图片,主要看美工了,美工要规定那个图片大小,我这里是随便放的
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                drawerLayout.openDrawer(Gravity.LEFT);
            }
        });
        navigationView.setNavigationItemSelectedListener(this);


    }
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.publish:
            //跳转的activity在这里建立
//                startActivity(new Intent(this, PublishActivity.class));
                drawerLayout.closeDrawers();
                return true;

          
            case R.id.published:
//                startActivity(new Intent(this, PublishedActivity.class));
                drawerLayout.closeDrawers();
                return true;

           
            case R.id.growth:
//                startActivity(new Intent(this, GrowthActivity.class));
                drawerLayout.closeDrawers();
                break;

    
            case R.id.collect:
//                startActivity(new Intent(this, CollectionActivity.class));
                drawerLayout.closeDrawers();
                return true;

            case R.id.setting:
//                startActivity(new Intent(this, SettingActivity.class));
                drawerLayout.closeDrawers();
                return true;
        }
        return super.onContextItemSelected(item);
    }
}

所以继承DrawerActivity的MainActivity的代码就十分的简洁了,而且其他的activity也可以继承ToolbarActivity从而实现复用了。

public class MainActivity extends DrawerActivity {
    private static final int PAGE_LIMIT = 4;//这里是限定预加载的页面个数,不用Fragment重新去onCreate。使用这个就是去优化页面,从而viewpager去缓存view
    @BindView(R.id.tab_layout)
    TabLayout tabLayout;
    @BindView(R.id.viewpager)
    NoScrollViewPager viewPager;


    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected void initView() {

    }

    @Override
    protected void initEvent() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            String packageName = getPackageName();
            PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
            if (!pm.isIgnoringBatteryOptimizations(packageName)) {
                Intent intent = new Intent();
                intent.setAction(android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                intent.setData(Uri.parse("package:" + packageName));
                startActivity(intent);
            }
        }
    }

    @Override
    protected void loadData() {
        List<Fragment> fragments = new ArrayList<>();
        fragments.add(new AbilityFragment());
        fragments.add(new AttentionFragment());
        fragments.add(new DiscoveryFragment());
        fragments.add(new ArenaFragment());
        ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager(), fragments);
        viewPager.setAdapter(adapter);
        viewPager.setOffscreenPageLimit(PAGE_LIMIT);
        tabLayout.setupWithViewPager(viewPager);
        for (int i = 0; i < tabLayout.getTabCount(); i++) {
            TabLayout.Tab tab = tabLayout.getTabAt(i);
            Drawable drawable = null;
            switch (i) {
                case 0:
                    drawable = ContextCompat.getDrawable(this, R.drawable.tab_item_home);
                    break;
                case 1:
                    drawable = ContextCompat.getDrawable(this, R.drawable.tab_item_attention);
                    break;
                case 2:
                    drawable = ContextCompat.getDrawable(this, R.drawable.tab_item_discovery);
                    break;
                case 3:
                    drawable = ContextCompat.getDrawable(this, R.drawable.tab_item_arena);
                    break;
            }
            if (tab != null) {
                tab.setIcon(drawable);
            }
        }
    }
}

接下来我们看下fragment的封装:

这里写图片描述

同样的,我们可以看到我写了三个抽象fragment,分别负责不同的业务。

1.BaseFragment。主要有模板模式的方法,fragment的懒加载

解析懒加载: setUserVisibleHint方法用于告诉系统,这个Fragment的UI是否是可见的。所以我们只需要继承Fragment并重写该方法,即可实现在fragment可见时才进行数据加载操作,即Fragment的懒加载。

public abstract class BaseFragment extends Fragment {
    private Handler handler = new Handler();
    private Runnable loadDataTask = new Runnable() {
        @Override
        public void run() {
            loadData();
        }
    };
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(getLayoutId(), container, false);
        ButterKnife.bind(this, view);
        return view;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        initView();
        initEvent();
        // 延迟加载数据,减少卡顿
        handler.postDelayed(loadDataTask, 500);
    }
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        //做了个判断,判断isVisibleToUser只有为true,才去执行loadData()方法加载网络(或本地)数据。
        if (!isVisibleToUser) {
            handler.removeCallbacks(loadDataTask);
        }
    }

    protected abstract void initView();

    protected abstract void initEvent();

    protected abstract void loadData();

    protected abstract int getLayoutId();
}

2.RecyclerViewFragment,因为RecyclerView在多列表app中占很多部分,所以我抽离它作为一层fragment来编写,方便代码复用。还有此fragment还要完成下拉刷新的消息通知,子类实现复用。

public abstract class RecyclerViewFragment extends BaseFragment implements SwipeRefreshLayout.OnRefreshListener {

    protected static final int REFRESH = 0x101;
    @BindView(R.id.recycler_view)
    RecyclerView recyclerView;
    @BindView(R.id.swipeRefreshLayout)
    SwipeRefreshLayout swipeRefreshLayout;
    protected RecyclerView.Adapter adapter;
//一个handle让子类覆写onrefresh去通知父类,然后父类调用handler实现复用。
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case REFRESH:
                    swipeRefreshLayout.setRefreshing(false);
                    break;
            }
        }
    };
    @Override
    protected void initView() {
        FullyLinearLayoutManager layoutManager = new FullyLinearLayoutManager(getActivity(), LinearLayout.VERTICAL, false);
        layoutManager.setSmoothScrollbarEnabled(true);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setNestedScrollingEnabled(false);


        swipeRefreshLayout.setSize(SwipeRefreshLayout.LARGE);
        swipeRefreshLayout.setColorSchemeResources(R.color.colorArenaGreen, R.color.colorAccent, R.color.colorMainDividerLine);

    }
    @Override
    protected void initEvent() {
        swipeRefreshLayout.setOnRefreshListener(this);
    }

    @Override
    protected void loadData(){
        adapterBuilder();
        recyclerView.setAdapter(adapter);
    }
    //子activity设置适配器
    protected abstract void adapterBuilder();
    @Override
    public void onRefresh() {
        //todo 网络请求任务
        handler.sendEmptyMessageDelayed(REFRESH, 3000);
    }
}

3.BannerFragment主要完成轮播图的封装,考虑广告众多app提供了一层。

public abstract class BannerFragment extends RecyclerViewFragment implements OnItemClickListener {

    private static final long LOOP_TIME = 5000;

//此轮播使用的是第三方的控件,实际项目开发时为了方便而使用的一个库。
    @BindView(R.id.banner)
    ConvenientBanner<String> convenientBanner;

    protected abstract List<String> getBitmapList();
    protected void loadConvenientBanner() {
        convenientBanner.setPages(new CBViewHolderCreator<LocalImageHolderView>() {
            @Override
            public LocalImageHolderView createHolder() {
                return new LocalImageHolderView();
            }
        }, getBitmapList())
                .setPageIndicator(new int[]{R.drawable.page_indicator, R.drawable.page_indicator_focused})
                .setOnItemClickListener(this);
        convenientBanner.startTurning(LOOP_TIME);
    }

    @Override
    protected void initView() {
        super.initView();
        convenientBanner.setPages(new CBViewHolderCreator<LocalImageHolderView>() {
            @Override
            public LocalImageHolderView createHolder() {
                return new LocalImageHolderView();
            }
        }, getBitmapList())
                .setPageIndicator(new int[]{R.drawable.page_indicator, R.drawable.page_indicator_focused})
                .setOnItemClickListener(this);
        convenientBanner.startTurning(LOOP_TIME);
    }

    @Override
    protected void loadData(){
        super.loadData();
    }
    public class LocalImageHolderView implements Holder<String> {
        private ImageView imageView;

        @Override
        public View createView(Context context) {
            imageView = new ImageView(context);
            imageView.setScaleType(ImageView.ScaleType.FIT_XY);
            return imageView;
        }

        @Override
        public void UpdateUI(Context context, final int position, String data) {
            Glide.with(context).load(data).into(imageView);
        }
    }
}

源码下载:Android-多列表的项目(Rxjava+Rtrofit+Recyclerview+Glide+Adapter封装)

好了,Android-多列表的项目(Rxjava+Rtrofit+Recyclerview+Glide+Adapter封装)之(一)项目架构讲完了。本博客是这个系列的第一篇,所以讲得是像项目的框架而已。另外,这个系列会逐步更新,我会尽快出完给大家,分享经验给大家。欢迎在下面指出错误,共同学习!!你的点赞是对我最好的支持!!

转载请注明:【JackFrost的博客】

更多内容,可以访问JackFrost的博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,398评论 25 707
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,702评论 22 664
  • 2017年的暑假将要来临,我们为一些家庭条件困难的大学生提供了便利的暑假兼职工作,那就是❤️清华大学❤️❤️北京大...
    清华晓亮阅读 868评论 0 0
  • 会话技术 “HTTP协议”是无状态协议HTTP协议不能告诉我们多请求是否是来自同一个人。 会话控制会话控制的思想就...
    dptms阅读 992评论 0 1
  • 前几天和胡老板聊起我们这一代人在北上广深踟蹰前行即将奔三时的焦虑,不可避免地说到了职业规划这个话题。 对我们这些刚...
    Vicol_略设小计阅读 578评论 5 12