Android 组件化填坑

01.Fragment通信难点

  • 在网上看到很多博客说,如何拆分组件,按模块拆分,或者按照功能拆分。但很少有提到fragment在拆分组件时的疑问,这个让我很奇怪。
  • 先来说一个业务需求,比如一个购物商城app,有4个模块,做法一般是一个activity+4个fragment,这个大家都很熟悉,这四个模块分别是:首页,发现,购物车,我的。然后这几个页面是用fragment写的,共用一个宿主activity,那么在做组件化的时候,我想把它按照业务拆分成首页,发现,购物车和我的四个独立的业务模块。
阅读更多

Android 组件化案例

01.组件化实践开源项目

  • 关于组件化开发一点感想
    • 关于网上有许多关于组件化的博客,讲解了什么是组件化,为何要组件化,以及组件化的好处。大多数文章提供了组件化的思路,给我着手组件化开发提供了大量的便利。感谢前辈大神的分享!虽然有一些收获,但是很少有文章能够给出一个整体且有效的方案,或者一个具体的Demo。
    • 但是毕竟看博客也是为了实践做准备,当着手将之前的开源案例改版成组件化案例时,出现了大量的问题,也解决了一些问题。主要是学些了组件化开发流程。
    • 大多数公司慢慢着手组件化开发,在小公司,有的人由于之前没有做过组件化开发,尝试组件化也是挺好的;在大公司,有的人一去只是负责某个模块,可能刚开始组件化已经有人弄好了,那学习实践组件化那更快一些。业余实践,改版之前开源项目,写了这篇博客,耗费我不少时间,要是对你有些帮助,那我就很开心呢。由于我也是个小人物,所以写的不好,勿喷,欢迎提出建议!
阅读更多

Android 组件化框架

01.传统APP架构图

  • 传统APP架构图
    • 如图所示,从网上摘来的……
    • image
  • 存在的问题
    • 普遍使用的 Android APP 技术架构,往往是在一个界面中存在大量的业务逻辑,而业务逻辑中充斥着各种网络请求、数据操作等行为,整个项目中也没有模块的概念,只有简单的以业务逻辑划分的文件夹,并且业务之间也是直接相互调用、高度耦合在一起的。单一工程模型下的业务关系,总的来说就是:你中有我,我中有你,相互依赖,无法分离。如下图:
阅读更多

Android 组件化概念

01.什么是组件化

  • 什么是组件化呢?
    • 组件(Component)是对数据和方法的简单封装,功能单一,高内聚,并且是业务能划分的最小粒度。
    • 组件化是基于组件可重用的目的上,将一个大的软件系统按照分离关注点的形式,拆分成多个独立的组件,使得整个软件系统也做到电路板一样,是单个或多个组件元件组装起来,哪个组件坏了,整个系统可继续运行,而不出现崩溃或不正常现象,做到更少的耦合和更高的内聚。

02.区分模块化与组件化

阅读更多

Android 为何组件化

01.为何组件化

  • APP迭代维护成本增高
    • 投资界,新芽,项目工厂等APP自身在飞速发展,版本不断迭代,新功能不断增加,业务模块数量不断增加,业务上的处理逻辑越变越复杂,同时每个模块代码也变得越来越多,这就引发一个问题,所维护的代码成本越来越高,稍微一改动可能就牵一发而动全身,改个小的功能点就需要回归整个APP测试,这就对开发和维护带来很大的挑战。
  • 多人组合需要组件化
    • APP 架构方式是单一工程模式,业务规模扩大,随之带来的是团队规模扩大,那就涉及到多人协作问题,每个移动端软件开发人员势必要熟悉如此之多代码,如果不按照一定的模块组件机制去划分,将很难进行多人协作开发,随着单一项目变大,而且Andorid项目在编译代码方面就会变得非常卡顿,在单一工程代码耦合严重,每修改一处代码后都需要重新编译打包测试,导致非常耗时。
阅读更多

Android ScrollView 嵌套 RecyclerView 问题

01.可能出现的问题

  • 实际开发中,要实现在NestedScrollView中嵌入一个或多个RecyclerView。这一做法通常会导致如下几个问题
    • 页面滑动卡顿
    • ScrollView高度显示不正常
    • RecyclerView内容显示不全
    • NestedScrollView滚动不到顶部

02.滑动卡顿问题

阅读更多

Android RecyclerView 滑动冲突

01.如何判断RecyclerView控件滑动到顶部和底部

  • 有一种使用场景,购物商城的购物车页面,当RecyclerView滑动到顶部时,让刷新控件消费事件;当RecyclerView滑动到底部时,让下一页控件[猜你喜欢]消费事件。

    • 代码如下所示:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    public class VerticalRecyclerView extends RecyclerView {

    private float downX;
    private float downY;
    /** 第一个可见的item的位置 */
    private int firstVisibleItemPosition;
    /** 第一个的位置 */
    private int[] firstPositions;
    /** 最后一个可见的item的位置 */
    private int lastVisibleItemPosition;
    /** 最后一个的位置 */
    private int[] lastPositions;
    private boolean isTop;
    private boolean isBottom;

    public VerticalRecyclerView(Context context) {
    this(context, null);
    }

    public VerticalRecyclerView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    }

    public VerticalRecyclerView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
    LayoutManager layoutManager = getLayoutManager();
    if (layoutManager != null) {
    if (layoutManager instanceof GridLayoutManager) {
    lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
    firstVisibleItemPosition = ((GridLayoutManager) layoutManager).findFirstVisibleItemPosition();
    } else if (layoutManager instanceof LinearLayoutManager) {
    lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
    firstVisibleItemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
    } else if (layoutManager instanceof StaggeredGridLayoutManager) {
    StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
    if (lastPositions == null) {
    lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
    firstPositions = new int[staggeredGridLayoutManager.getSpanCount()];
    }
    staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
    staggeredGridLayoutManager.findFirstVisibleItemPositions(firstPositions);
    lastVisibleItemPosition = findMax(lastPositions);
    firstVisibleItemPosition = findMin(firstPositions);
    }
    } else {
    throw new RuntimeException("Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager");
    }

    switch (ev.getAction()) {
    case MotionEvent.ACTION_DOWN:
    downX = ev.getX();
    downY = ev.getY();
    //如果滑动到了最底部,就允许继续向上滑动加载下一页,否者不允许
    getParent().requestDisallowInterceptTouchEvent(true);
    break;
    case MotionEvent.ACTION_MOVE:
    float dx = ev.getX() - downX;
    float dy = ev.getY() - downY;
    boolean allowParentTouchEvent;
    if (Math.abs(dy) > Math.abs(dx)) {
    if (dy > 0) {
    //位于顶部时下拉,让父View消费事件
    allowParentTouchEvent = isTop = firstVisibleItemPosition == 0 && getChildAt(0).getTop() >= 0;
    } else {
    //位于底部时上拉,让父View消费事件
    int visibleItemCount = layoutManager.getChildCount();
    int totalItemCount = layoutManager.getItemCount();
    allowParentTouchEvent = isBottom = visibleItemCount > 0 && (lastVisibleItemPosition) >= totalItemCount - 1 && getChildAt(getChildCount() - 1).getBottom() <= getHeight();
    }
    } else {
    //水平方向滑动
    allowParentTouchEvent = true;
    }
    getParent().requestDisallowInterceptTouchEvent(!allowParentTouchEvent);
    }
    return super.dispatchTouchEvent(ev);

    }

    private int findMax(int[] lastPositions) {
    int max = lastPositions[0];
    for (int value : lastPositions) {
    if (value >= max) {
    max = value;
    }
    }
    return max;
    }

    private int findMin(int[] firstPositions) {
    int min = firstPositions[0];
    for (int value : firstPositions) {
    if (value < min) {
    min = value;
    }
    }
    return min;
    }

    public boolean isTop() {
    return isTop;
    }

    public boolean isBottom() {
    return isBottom;
    }
    }
阅读更多

Android RecyclerView 问题汇总

01.Recyclerview.getLayoutPosition()问题

  • 在RecycleView中的相关方法中,有两种类型的位置

    • 布局位置:从LayoutManager的角度看,条目在最新布局计算中的位置。
      • 返回布局位置的方法使用最近一次布局运算后的位置,如getLayoutPosition()和findViewHolderForLayoutPosition(int)。这些位置包含了最近一次布局运算后的变化。你可以根据这些位置来与用户正在屏幕上看到的保持一致。比如,你有一个条目列表,当用户请求第5个条目时,你可以使用这些方法来匹配用户看到的。
阅读更多

Android RecyclerView 优化处理

01.RecyclerView滑动卡顿

  • RecyclerView滑动卡顿的原因有哪些?
    • 第一种:嵌套布局滑动冲突
      • 导致嵌套滑动难处理的关键原因在于当子控件消费了事件, 那么父控件就不会再有机会处理这个事件了, 所以一旦内部的滑动控件消费了滑动操作, 外部的滑动控件就再也没机会响应这个滑动操作了
    • 第二种:嵌套布局层次太深,比如六七层等
      • 测量,绘制布局可能会导致滑动卡顿
阅读更多