前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >RecyclerView定制:通用ItemDecoration及全展开RecyclerView的实现不同场景RecyclerView实现

RecyclerView定制:通用ItemDecoration及全展开RecyclerView的实现不同场景RecyclerView实现

作者头像
看书的小蜗牛
发布于 2018-06-29 07:50:39
发布于 2018-06-29 07:50:39
2.7K00
代码可运行
举报
运行总次数:0
代码可运行

Android L面世之后,Google就推荐在开发项目中使用RecyclerView来取代ListView,因为RecyclerView的灵活性跟性能都要比ListView更强,但是,带来的问题也不少,比如:列表分割线都要开发者自己控制,再者,RecyclerView的测量与布局的逻辑都委托给了自己LayoutManager来处理,如果需要对RecyclerView进行改造,相应的也要对其LayoutManager进行定制。本文主要就以以下场景给出RecyclerView使用参考:

RecyclerView的几种常用场景

  • 如何实现带分割线的列表式RecyclerView
  • 如何实现带分割线网格式RecyclerView
  • 如何实现全展开的列表式RecyclerView(比如:嵌套到ScrollView中使用)
  • 如何实现全展开的网格式RecyclerView(比如:嵌套到ScrollView中使用)

先看一下实现样式,为了方便控制,边界的均不设置分割线,方便定制,如果需要可以采用Padding或者Margin来实现。Github连接 RecyclerItemDecoration

网格式列表样式

全展开的网格式列表

全展开的线性列表

不同场景RecyclerView实现

默认的纵向列表式RecyclerView

首先看一下最简单的纵向线性RecyclerView,一般用以下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
    linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    mRecyclerView.setLayoutManager(linearLayoutManager);

以上就是最简单的线性RecyclerView的实现,但默认不带分割线,如果想要使用比如20dp的黑色作为分割线,就需要自己定制,Google为RecyclerView提供了ItemDecoration,它的作用就是为Item添加一些附属信息,比如:分割线,浮层等。

带分割线的列表式RecyclerView--LinearItemDecoration

RecyclerView提供了addItemDecoration接口与ItemDecoration类用来定制分割线样式,那么,在RecyclerView源码中,是怎么用使用ItemDecoration的呢。与普通View的绘制流程一致,RecyclerView也要经过measure->layout->draw,并且在measure、layout之后,就应该按照ItemDecoration的限制,为RecyclerView的分割线挪出空间。RecyclerView的measure跟Layout其实都是委托给自己的LayoutManager的,在LinearLayoutManager测量或者布局时都会直接或者间接调用RecyclerView的measureChildWithMargins函数,而measureChildWithMargins函数会进一步找到addItemDecoration添加的ItemDecoration,通过其getItemOffsets函数获取所需空间信息,源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
      final LayoutParams lp = (LayoutParams) child.getLayoutParams();

      final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
      widthUsed += insets.left + insets.right;
      heightUsed += insets.top + insets.bottom;

      final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
              getPaddingLeft() + getPaddingRight() +
                      lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
              canScrollHorizontally());
      final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
              getPaddingTop() + getPaddingBottom() +
                      lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
              canScrollVertically());
      if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
          child.measure(widthSpec, heightSpec);
      }
  }

可见measureChildWithMargins会首先通过getItemDecorInsetsForChild计算出每个child的ItemDecoration所限制的边界信息,之后将边界所需的空间作为已用空间为child构造MeasureSpec,最后用MeasureSpec对child进行尺寸测量:child.measure(widthSpec, heightSpec);来看一下getItemDecorInsetsForChild函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Rect getItemDecorInsetsForChild(View child) {
    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
    if (!lp.mInsetsDirty) {
        return lp.mDecorInsets;
    }

    final Rect insets = lp.mDecorInsets;
    insets.set(0, 0, 0, 0);
    final int decorCount = mItemDecorations.size();
    for (int i = 0; i < decorCount; i++) {
        mTempRect.set(0, 0, 0, 0);
        <!--通过这里知道,需要绘制的空间位置-->
        mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
        insets.left += mTempRect.left;
        insets.top += mTempRect.top;
        insets.right += mTempRect.right;
        insets.bottom += mTempRect.bottom;
    }
    lp.mInsetsDirty = false;
    return insets;
}

一般而言,不会同时设置多类ItemDecoration,太麻烦,对于普通的线性布局列表,其实就简单设定一个自定义ItemDecoration即可,其中outRect参数主要是控制每个Item上下左右的分割线所占据的宽度跟高度,这个尺寸跟绘制的时候的尺寸应该对应(如果需要绘制的话),看一下LinearItemDecoration的getItemOffsets实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    if (mOrientation == VERTICAL_LIST) {
    
    <!--垂直方向 ,最后一个不设置padding-->
        if (parent.getChildAdapterPosition(view) < parent.getAdapter().getItemCount()1) {
            outRect.set(0, 0, 0, mSpanSpace);
        } else {
            outRect.set(0, 0, 0, 0);
        }
    } else {
     <!--水平方向 ,最后一个不设置padding-->
        if (parent.getChildAdapterPosition(view) < parent.getAdapter().getItemCount()1) {
            outRect.set(0, 0, mSpanSpace, 0);
        } else {
            outRect.set(0, 0, 0, 0);
        }
    }
}   

measure跟layout之后,再来看一下RecyclerView的onDraw函数, RecyclerView在onDraw函数中会调用ItemDecoration的onDraw,绘制分割线或者其他辅助信息,ItemDecoration 支持上下左右四个方向定制占位分割线等信息,具体要绘制的样式跟位置都完全由开发者确定,所以自由度非常大,其实如果不是太特殊的需求的话,onDraw函数完全可以不做任何处理,仅仅用背景色就可以达到简单的分割线的目的,当然,如果想要定制一些特殊的图案之类的需话,就需要自己绘制,来看一下LinearItemDecoration的onDraw(只看Vertical的)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    if (mOrientation == VERTICAL_LIST) {
        drawVertical(c, parent);
    } else {
       ...
    }
}

其实,如果不是特殊的绘制需求,比如显示七彩的,或者图片,完全不需要任何绘制,如果一定要绘制,注意绘制的尺寸区域跟原来getItemOffsets所限制的区域一致,绘制的区域过大不仅不会显示出来,还会引起过度绘制的问题:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void drawVertical(Canvas c, RecyclerView parent) {                              int totalCount = parent.getAdapter().getItemCount();            final int childCount = parent.getChildCount();              for (int i = 0; i < childCount; i++) {                  final View child = parent.getChildAt(i);                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child                          .getLayoutParams();                 final int top = child.getBottom() + params.bottomMargin +                           Math.round(ViewCompat.getTranslationY(child));                  final int bottom = top + mVerticalSpan;                         final int left = child.getLeft() + params.leftMargin;                   final int right = child.getRight() + params.rightMargin;                        if (!isLastRaw(parent, i, mSpanCount, totalCount))                      if (childCounti > mSpanCount) {                         drawable.setBounds(left, top, right, bottom);                           drawable.draw(c);            }       
    }       
}

带分割线的网格式RecyclerView--GridLayoutItemDecoration

网格式RecyclerView的处理流程跟上面的线性列表类似,不过网格式的需要根据每个Item的位置为其设置好边距,比如最左面的不需要左边占位,最右面的不需要右面的占位,最后一行不需要底部的占位,如下图所示

网格式ItemDocration的限制

RecyclerView的每个childView都会通过getItemOffsets来设置自己ItemDecoration,对于网格式的RecyclerView,需要在四个方向上对其ItemDecoration进行限制,来看一下其实现类GridLayoutItemDecoration的getItemOffsets:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    final int position = parent.getChildAdapterPosition(view);
    final int totalCount = parent.getAdapter().getItemCount();
    int left = (position % mSpanCount == 0) ? 0 : mHorizonSpan;
    int bottom = ((position + 1) % mSpanCount == 0) ? 0 : mVerticalSpan;
    if (isVertical(parent)) {
        if (!isLastRaw(parent, position, mSpanCount, totalCount)) {
            outRect.set(left, 0, 0, mVerticalSpan);
        } else {
            outRect.set(left, 0, 0, 0);
        }
    } else {
        if (!isLastColumn(parent, position, mSpanCount, totalCount)) {
            outRect.set(0, 0, mHorizonSpan, bottom);
        } else {
            outRect.set(0, 0, 0, bottom);
        }
    }
}

其实上面的代码就是根据RecyclerView滑动方向(横向或者纵向)以及child的位置(是不是最后一行或者最后一列),对附属区域进行限制,同样,如果不是特殊的分割线样式,通过背景就基本可以实现需求,不用特殊draw。

全展开的列表式RecyclerView--ExpandedLinearLayoutManager

RecyclerView全展开的逻辑跟分割线不同,全展开主要是跟measure逻辑相关,简单看一下RecyclerView(v-22版本,相对简单)的measure源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
        ...
        
        <!--关键代码,如果mLayout(LayoutManager)非空,就采用LayoutManager的mLayout.onMeasure-->
    if (mLayout == null) {
        defaultOnMeasure(widthSpec, heightSpec);
    } else {
        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
    }

    mState.mInPreLayout = false; // clear
}

由以上代码可以看出,在为RecyclerView设置了LayoutManager之后,RecyclerView的measure逻辑其实就是委托给了它的LayoutManager,这里以LinearLayoutManager为例,不过LinearLayoutManager源码里面并没有重写onMeasure函数,也就是说,对于RecyclerView的线性样式,对于尺寸的处理采用的是跟ViewGroup一样的处理,完全由父控件限制,不过对于v-23里面有了一些修改,就是增加了对wrap_content的支持。既然这样,我们就可以把设置尺寸的时机放到LayoutManager的onMeasure中,对全展开的RecyclerView来说,其实就是将所有child测量一遍,之后将每个child需要高度或者宽度累加,看一下ExpandedLinearLayoutManager的实现:在测量child的时候,采用RecyclerView的measureChildWithMargins,该函数已经将ItemDecoration的占位考虑进去,之后通过getDecoratedMeasuredWidth获取真正需要占用的尺寸。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
                      int widthSpec, int heightSpec) {
    final int widthMode = View.MeasureSpec.getMode(widthSpec);
    final int heightMode = View.MeasureSpec.getMode(heightSpec);
    final int widthSize = View.MeasureSpec.getSize(widthSpec);
    final int heightSize = View.MeasureSpec.getSize(heightSpec);
    int measureWidth = 0;
    int measureHeight = 0;
    int count;
    if (mMaxItemCount < 0 || getItemCount() < mMaxItemCount) {
        count = getItemCount();
    } else {
        count = mMaxItemCount;
    }
    for (int i = 0; i < count; i++) {
        int[] measuredDimension = getChildDimension(recycler, i);
        if (measuredDimension == null || measuredDimension.length != 2)
            return;
        if (getOrientation() == HORIZONTAL) {
            measureWidth = measureWidth + measuredDimension[0];
           <!--获取最大高度-->
            measureHeight = Math.max(measureHeight, measuredDimension[1]);
        } else {
            measureHeight = measureHeight + measuredDimension[1];
            <!--获取最大宽度-->
            measureWidth = Math.max(measureWidth, measuredDimension[0]);
        }
    }

    measureHeight = heightMode == View.MeasureSpec.EXACTLY ? heightSize : measureHeight;
    measureWidth = widthMode == View.MeasureSpec.EXACTLY ? widthSize : measureWidth;
    if (getOrientation() == VERTICAL && measureWidth > widthSize) {
        measureWidth = widthSize;
    } else if (getOrientation() == HORIZONTAL && measureHeight > heightSize) {
        measureHeight = heightSize;
    }
    setMeasuredDimension(measureWidth, measureHeight);
}


private int[] getChildDimension(RecyclerView.Recycler recycler, int position) {
    try {
        int[] measuredDimension = new int[2];
        View view = recycler.getViewForPosition(position);
        //测量childView,以便获得宽高(包括ItemDecoration的限制)
        super.measureChildWithMargins(view, 0, 0);
        //获取childView,以便获得宽高(包括ItemDecoration的限制),以及边距
        RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
        measuredDimension[0] = getDecoratedMeasuredWidth(view) + p.leftMargin + p.rightMargin;
        measuredDimension[1] = getDecoratedMeasuredHeight(view) + p.bottomMargin + p.topMargin;
        return measuredDimension;
    } catch (Exception e) {
        Log.d("LayoutManager", e.toString());
    }
    return null;
}

全展开的网格式RecyclerView--ExpandedGridLayoutManager

全展开的网格式RecyclerView的实现跟线性的十分相似,唯一不同的就是在确定尺寸的时候,不是将每个child的尺寸叠加,而是要将每一行或者每一列的尺寸叠加,这里假定行高或者列宽都是相同的,其实在使用中这两种场景也是最常见的,看如下代码,其实除了加了行与列判断逻辑,其他基本跟上面的全展开线性的类似。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 @Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
    final int widthMode = View.MeasureSpec.getMode(widthSpec);
    final int heightMode = View.MeasureSpec.getMode(heightSpec);
    final int widthSize = View.MeasureSpec.getSize(widthSpec);
    final int heightSize = View.MeasureSpec.getSize(heightSpec);
    int measureWidth = 0;
    int measureHeight = 0;
    int count = getItemCount();
    int span = getSpanCount();
    for (int i = 0; i < count; i++) {
        measuredDimension = getChildDimension(recycler, i);
        if (getOrientation() == HORIZONTAL) {
            if (i % span == 0 ) {
                measureWidth = measureWidth + measuredDimension[0];
            }
            measureHeight = Math.max(measureHeight, measuredDimension[1]);
        } else {
            if (i % span == 0) {
                measureHeight = measureHeight + measuredDimension[1];
            }
            measureWidth = Math.max(measureWidth, measuredDimension[0]);
        }
    }
    measureHeight = heightMode == View.MeasureSpec.EXACTLY ? heightSize : measureHeight;
    measureWidth = widthMode == View.MeasureSpec.EXACTLY ? widthSize : measureWidth;
    setMeasuredDimension(measureWidth, measureHeight);
}

最后附上横向滑动效果图:

横向滑动

以上就是比较通用的RecyclerView使用场景及所做的兼容 ,最后附上Github链接RecyclerItemDecoration,欢迎star,fork。

作者:看书的小蜗牛

原文链接: RecyclerView定制:通用ItemDecoration及全展开RecyclerView的实现

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016.12.27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
张一鸣发牢骚,马化腾硬气回应,俞永福却支持!TT大战一触即发?
张一鸣宣布“庆祝一个小的胜利”,一季度,抖音(国际版名为Tik Tok)在App Store的全球下载量达到第一,超过了Youtube、WeChat诸多平台。张一鸣同时表示,“微信的借口封杀、微视的抄袭搬运,挡不住抖音的步伐。”对此,马化腾表示“可以理解为诽谤”,张一鸣回应称“前者不适合讨论,后者一直在公证,我没有想口水战,刚刚没忍住发了个牢骚”,马化腾回应称“要公证你们的太多了”。两者对话火药味十足。
罗超频道
2018/07/23
6170
张一鸣发牢骚,马化腾硬气回应,俞永福却支持!TT大战一触即发?
小程序+公众号玩出新花样?有趣skr了
微信也是一张流量温床,奈何转化能力一般,而小程序的出现结合小程序一起,从公众号呈现内容,到引流吸粉,再到转化成交,很大程度上帮助了商家解决了高成本问题。
微盛企微管家
2018/09/17
1.2K0
小程序+公众号玩出新花样?有趣skr了
微信早报 | 一个公众号可支持多个赞赏账户;小程序「西瓜足迹」因侵权被封
微信互联网人每天必看的早新闻 小程序 1. 6 月 6 日,曾刷遍朋友圈的小程序「西瓜足迹」被微信方面暂停服务,原因为「侵犯他人合法权益」。 2. 6 月 5 日,腾讯推出小程序产品「金融风险查询举报中心」,该小程序中主要针对传销骗局进行曝光,普洱币、中非币等曾被媒体曝光的虚拟币传销骗局被收录其中。 3. 6 月 6 日,中国太保产险与腾讯微保共同发布保险红包小程序。该小程序主要以保险红包的形式进行意外险保额的赠送。 4. 据悉,微信在马来西亚推出本地钱包「令吉钱包
知晓君
2018/06/28
1.5K0
微信早报 | 腾讯注册「小程序」商标仍失败;抖音不受微信新外链规则影响
微信互联网人每天必看的早新闻。 小程序 1. 星巴克联合微信支付和腾讯微视上线了 「520 用星巴克说」小程序。使用该小程序,语音或者文字留下暗号,有机会获得星巴克代金券。 2. 5 月 18 日,腾
知晓君
2018/06/28
9160
独家爆料!全国第一个「扫码乘车」小程序,背后竟有这些黑科技
一周前,知晓程序报道了全国第一个「扫码乘车」的小程序,瞬间引起全国各地粉丝的关注。
知晓君
2018/07/30
1.6K0
微信还能打败今日头条吗?「看一看」想试试 | 微观
互联网的新老更替虽快,却似乎仍走不出 BAT 的阴影。 产品能力强大如今日头条,张一鸣也足足花了 6 年的时间,才能够成功吸引马化腾的注意,然后双方粉面含羞地骂上一仗。 但是,相比较马化腾与张一鸣的互怼,如果能来一次张小龙与张一鸣的坐而论道或许会更有意思些,人类天才与人工智能的较量,太符合眼下的主题了。而除了微视与抖音的 PK,从产品层面上来说,一路过关斩将的今日头条,也终于有机会吸引到产品之神张小龙的注意了。在此前流出的微信内部视频中,张小龙就表示: 今日头条是强大对手,但看一看不一样。 的确,今日头条已
知晓君
2018/06/28
6110
微信早报 | 抖音公众号开撕腾讯;蚂蚁金服投资者不可投腾讯系
微信互联网人每天必看的早新闻 作者:冷思真 小程序 1. 5 月 22 日,百度 app 业务部总经理平晓黎表示,百度将于今年 7 月正式推出百度智能小程序。百度将会对开发者开放百度全域流量并提供
知晓君
2018/06/28
7750
腾讯梦想乌龙始末:百万+爆款、干掉同题的潘乱和坐飞机的张军
五月第一个周末因为一篇《腾讯没有梦想》的文章,科技圈高潮迭起。当所有人都以为剧终时,一位网名为Zen的人在公众号发布的《腾讯的梦想其实是我PS出来的》一文,很像是电影片尾曲后的彩蛋,模仿企业高层讲话混肴视听、哗众取宠,并堂而皇之地以“运营好奇心”的名义讲出来,确实不雅,不过,这也让整个事件更加充满戏剧性。
罗超频道
2018/07/23
8510
腾讯梦想乌龙始末:百万+爆款、干掉同题的潘乱和坐飞机的张军
【晓头条】公众号可群发小程序卡片 / 企业微信推出「名片夹」小程序 / 腾讯与故宫合作推出创新实验室
12 月 1 日,全国首个长江索道乘车码在山城重庆正式启用上线,乘客只需通过微信小程序中的「腾讯乘车码」,将二维码靠近索道入口的闸机,0.2 秒内即可入闸。
知晓君
2018/07/27
9670
微信早报 | FIFA 世界杯官方小程序上线;唯品会加入「快应用」首批流量扶持计划
微信互联网人每天必看的早新闻 小程序 1. 「微信公开课」发文,公布了小程序新增打开公众号文章功能、意见反馈等新能力。其同时表示开发者工具已更新,支持代码云端托管,并优化了开发版预览方式和界面布局。
知晓君
2018/06/28
9450
【晓头条】马化腾亲自为小程序站台 / 大量美妆号被要求改名 / 微信更新隐私指引,不同意不能用
此外,大量美妆公众号因名称涉及注册商标,被集中要求改名;微信最近也更新了隐私指引,如果不同意新的隐私指引,将无法继续使用微信。
知晓君
2018/07/30
9080
微信内测重磅新功能,广告主再添小程序推广渠道
双11前,小程序搜索入口突然开放了“商品”搜索功能;官方数据显示,小程序日交易量提升了142%,还有哪些大事,一起来看看吧~
中微信通
2018/11/05
1.5K0
【晓头条】腾讯告 4399 获赔五百万 / 今日头条被令整改 / 首张微信身份证在广州签发 / 小程序开放游戏类目
1. 小程序开放「游戏」类目,同步新增列表下拉小程序新入口。12 月 28 日,微信放出 6.6.1 版本更新,首次启动新版本,微信将会邀请玩家进入「跳一跳」小游戏,正式宣告小程序开放游戏类目。此外,从聊天列表页面下拉,可以直接查看、进入最近小程序。
知晓君
2018/07/26
9550
小程序一周报 | 新注册公众号将没有留言功能
微信团队为进一步规范公众平台生态环境,后续新注册的账号将没有留言功能,「最近三个月内注册,但尚未使用留言功能的账号将被收回留言权限。」
极乐君
2018/07/31
6720
小程序一周报 | 新注册公众号将没有留言功能
微信早报 | 抖音状告微信;公众号可以缴党费
微信互联网人每天必看的早新闻 小程序行业 1. 腾讯在第一季度的财报中称,超过三分之一的微信小游戏玩家在此之前并不属于腾讯游戏的玩家。 2. 近期,小游戏「海盗来了」的用户数量快速增长,日活跃玩家达到了 1500 万。 3. 相比上个季度,微信零售类小程序的 DAU 和每日交易量环比增长 50%。 腾讯官方认为小程序与 app 生态系统有协同作用,鼓励了许多非 app 开发人员接受移动互联网。 4. 京东近日开始内测一款基于微信平台的区块链小程序游戏「哈希庄园」,该游戏
知晓君
2018/06/28
1.2K0
腾讯和头条公关在朋友圈互怼,究竟在争什么?
11月17日,今日头条举办了一年一度的生机大会,新任CEO陈林首次亮相,宣布了今日头条内容生态升级。
罗超频道
2018/12/17
7180
干货|小程序运营:教你如何快速营销获客!
随着获客成本越来越高,裂变用户对于所有行业来说是非常大的一个难题。在流量成本越来越贵的当下,小程序电商的获客成本几乎为零,成为商家收割下一波电商增量市场的重要利器。
云店加小程序分享
2020/04/14
1.3K0
「小程序裂变」如何实现零成本获客?
当大家的微信好友在4000人以上时,假如你通过本行业的某位朋友引荐,新添加一位微信好友时,你去翻阅这位新好友的朋友圈,你多半会发现有共同好友点赞过Ta。
用户1745481
2019/01/09
7890
公众号已经老了,小程序永远年轻
在微信既有的产品哲学里面,毫无疑问,信息流是不被认可的。信息流意味着低劣;意味着平台参与内容生产,打扰了用户,干预了内容,也增加了平台方的压力与工作量。
知晓君
2018/07/26
1.2K0
重点解读:用小程序给公众号涨粉10w的7大行业案例
2017年1月9日,张小龙宣布小程序上线,到今天、刚好一周年; 期间陆陆续续出现了拼多多、摩拜单车、语音红包、头脑王者、心理测试等爆款小程序。今天来和大家聊聊如何利用好这巨大的流量洼地来给公众号涨粉。
疯狂的小程序
2018/01/23
5.5K1
推荐阅读
张一鸣发牢骚,马化腾硬气回应,俞永福却支持!TT大战一触即发?
6170
小程序+公众号玩出新花样?有趣skr了
1.2K0
微信早报 | 一个公众号可支持多个赞赏账户;小程序「西瓜足迹」因侵权被封
1.5K0
微信早报 | 腾讯注册「小程序」商标仍失败;抖音不受微信新外链规则影响
9160
独家爆料!全国第一个「扫码乘车」小程序,背后竟有这些黑科技
1.6K0
微信还能打败今日头条吗?「看一看」想试试 | 微观
6110
微信早报 | 抖音公众号开撕腾讯;蚂蚁金服投资者不可投腾讯系
7750
腾讯梦想乌龙始末:百万+爆款、干掉同题的潘乱和坐飞机的张军
8510
【晓头条】公众号可群发小程序卡片 / 企业微信推出「名片夹」小程序 / 腾讯与故宫合作推出创新实验室
9670
微信早报 | FIFA 世界杯官方小程序上线;唯品会加入「快应用」首批流量扶持计划
9450
【晓头条】马化腾亲自为小程序站台 / 大量美妆号被要求改名 / 微信更新隐私指引,不同意不能用
9080
微信内测重磅新功能,广告主再添小程序推广渠道
1.5K0
【晓头条】腾讯告 4399 获赔五百万 / 今日头条被令整改 / 首张微信身份证在广州签发 / 小程序开放游戏类目
9550
小程序一周报 | 新注册公众号将没有留言功能
6720
微信早报 | 抖音状告微信;公众号可以缴党费
1.2K0
腾讯和头条公关在朋友圈互怼,究竟在争什么?
7180
干货|小程序运营:教你如何快速营销获客!
1.3K0
「小程序裂变」如何实现零成本获客?
7890
公众号已经老了,小程序永远年轻
1.2K0
重点解读:用小程序给公众号涨粉10w的7大行业案例
5.5K1
相关推荐
张一鸣发牢骚,马化腾硬气回应,俞永福却支持!TT大战一触即发?
更多 >
LV.1
爱范儿知晓程序
目录
  • RecyclerView的几种常用场景
  • 不同场景RecyclerView实现
    • 默认的纵向列表式RecyclerView
    • 带分割线的列表式RecyclerView--LinearItemDecoration
    • 带分割线的网格式RecyclerView--GridLayoutItemDecoration
    • 全展开的列表式RecyclerView--ExpandedLinearLayoutManager
    • 全展开的网格式RecyclerView--ExpandedGridLayoutManager
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档