EffectiveAndroidUI 概述

邂逅

Android-CleanArchitecture 推荐,看到了这个略吊的项目名称EffectiveAndroidUI,3000+start果断把源码看遍。Android-CleanArchitecture 博文是Android初级架构的经典文章,建议有性趣的同学要看一下。

观后感

  • 项目使用不是dagger2,而是用的是square的dagger略显过时;
  • EffectiveAndroidUI 就是标题党,实践上是对ViewHolder抽象化,使用工厂(AdapterFactory)类来减小Adapter和ListUI界面的相互依赖关系。
    优点:使Adapter相对的独立,抽离ViewHolder中的事件处理,配合Presenter,易于维护,代码脉络相对清晰。
    缺点:较大的增加了代码的复杂度,建议项目初期不要使用,在项目进行架构优化是可以考虑引入。
  • 使用DraggablePanel 实现YouTube的视频拖拽浮动的功能,感觉略吊。
  • 像Android-CleanArchitecture使用了Navigator类来统一处理界面之间的导航关系
  • Renderers 从项目提取的开源库 减少Adapter 和 List之间的耦合 ;

代码走读

项目使用三种布局来分别适配了低版本(v10),常用版本(v11+), 及平板的布局。


这个很重要,因为俺就被这搞糊涂了,一开始跟着v11入口看代码,快看完的时候发现MainActivity.java 有一个Fragment的初始化

private void initializeTvShowFragment() {
        tvShowFragment = (TvShowFragment) getSupportFragmentManager().findFragmentById(R.id.f_tv_show);
    }

然后就,有点懵了....


现在开始从V11开始


MVP
TvShowCatalogFragment/TvShowDraggableFragment使用的是MVP模式,一句话概括就是:

明确(定义接口)UI界面的功能,把UI的交互逻辑放入Presenter实现。

如果接触过一些老项目的同学应该会深有感悟,随着项目功能的增加Activity/Fragment越来越重,耦合越来越强,使代码复用变得很难,无法积累及实现模块化,敏捷的开发。
故MVP就流行起来了,MVP还有一个好处就是方便Unit test。最新的Android Testing Support Library 也提倡使用MVP,在官方的Demo:android-testing也使用MVP,强烈建议同学们去看这个测试入门Demo。
进阶阅读:MVVM wiki ,还有官网的Data Binding Guide , 过一边较好。

重点代码举例
抽离MVP,TvShowCatalogFragment只有AdapterFactory比较重要

  private void initializeGridView() {
    adapter = tvShowRendererAdapterFactory.getTvShowRendererAdapter(tvShows);
    gv_tv_shows.setAdapter(adapter);
  }

TvShowRendererAdapterFactory.java

public class TvShowRendererAdapterFactory {

  private final TvShowRendererBuilder rendererBuilder;
  private final LayoutInflater layoutInflater;

  @Inject
  public TvShowRendererAdapterFactory(TvShowRendererBuilder rendererBuilder,
      LayoutInflater layoutInflater) {
    this.rendererBuilder = rendererBuilder;
    this.layoutInflater = layoutInflater;
  }

  public RendererAdapter<TvShow> getTvShowRendererAdapter(final TvShowCollection tvShowCollection) {
    return new RendererAdapter<TvShow>(layoutInflater, rendererBuilder, tvShowCollection);
  }
}

整个项目名称是EffectiveAndroidUI其实略有点夸张,这里突然想起大学外面的小菜馆名字“重庆大饭店”,“某某洗脚城”.....。Effective的体现:

EffectiveAndroid.jpg
RendererBuilder

核心是通过RendererAdapterFactory,T 数据泛华,RendererBuilder 来解强耦合

动画简述

动画实现的概述
动画主要通过使用ViewDragHelper, 界面布局使用一个Activity叠加两个Fragment来实现。 可以点击ViewDragHelper 简单初始化案例

DraggableView

初始化

  @Override protected void onFinishInflate() {
    super.onFinishInflate();
    if (!isInEditMode()) {
      mapGUI(); //DragView ListView 初始化
      initializeTransformer(); // Transform 管理类的初始化化
      initializeViewDragHelper();
    }
  }

重要方法的逻辑翻译

 private boolean smoothSlideTo(float slideOffset) {
    final int topBound = getPaddingTop();
    //(父控件参数 - dragView最小化参数) * 偏移因数 =  实践偏移量
    int x = (int) (slideOffset * (getWidth() - transformer.getMinWidthPlusMarginRight()));
    int y = (int) (topBound + slideOffset * getVerticalDragRange());
    if (viewDragHelper.smoothSlideViewTo(dragView, x, y)) {
      ViewCompat.postInvalidateOnAnimation(this);
      return true;
    }
    return false;
  }

onTouchEvent

  @Override public boolean onTouchEvent(MotionEvent ev) {
    ...
    if (activePointerId == INVALID_POINTER) { //是否是无效的点,是不做操作
      return false;
    }
    viewDragHelper.processTouchEvent(ev); //把事件交给ViewDragHelper处理,处理完触发回调
    if (isClosed()) { //是否是已经关闭,可拖动的View不在屏幕显示。是不做操作
      return false;
    }
    //event X, Y 加上parentView的坐标偏移,是否在ChildView 中
    boolean isDragViewHit = isViewHit(dragView, (int) ev.getX(), (int) ev.getY());
    boolean isSecondViewHit = isViewHit(secondView, (int) ev.getX(), (int) ev.getY());
    //Down事件和Up事件X坐标的变化量,当变化量大于指定值最大或最小化dragView
    analyzeTouchToMaximizeIfNeeded(ev, isDragViewHit);
    if (isMaximized()) {
      dragView.dispatchTouchEvent(ev);
    } else {
      dragView.dispatchTouchEvent(cloneMotionEventWithAction(ev, MotionEvent.ACTION_CANCEL));
    }
    return isDragViewHit || isSecondViewHit;
  }

clampViewPositionHorizontal

  @Override public int clampViewPositionHorizontal(View child, int left, int dx) {
    int newLeft = draggedView.getLeft();
    //当可以拖动的View最小化并且X轴的速度大于某个速度, 
    //或可以拖动的View在底部,但是没有靠右时更新left的值
    if ((draggableView.isMinimized() && Math.abs(dx) > MINIMUM_DX_FOR_HORIZONTAL_DRAG) || (
        draggableView.isDragViewAtBottom()
            && !draggableView.isDragViewAtRight())) {
      newLeft = left;
    }
    return newLeft;
  }

clampViewPositionVertical

  @Override public int clampViewPositionVertical(View child, int top, int dy) {
    int newTop = draggableView.getHeight() - draggableView.getDraggedViewHeightPlusMarginTop(); 
    //当可以拖动的View最小化并且Y轴的速度大于某个速度, 
    //或可以拖动的View在底部,但是没有靠右时更新Top的值
    if (draggableView.isMinimized() && Math.abs(dy) >= MINIMUM_DY_FOR_VERTICAL_DRAG
        || (!draggableView.isMinimized() && !draggableView.isDragViewAtBottom())) {

      final int topBound = draggableView.getPaddingTop();
      final int bottomBound = draggableView.getHeight()
          - draggableView.getDraggedViewHeightPlusMarginTop()
          - draggedView.getPaddingBottom();
    // bottomBound <= top <= topBound
      newTop = Math.min(Math.max(top, topBound), bottomBound);
    }
     return newTop;
  }

onViewPositionChanged change scale/alpha/position

triggerOnReleaseActionsWhileVerticalDrag

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

推荐阅读更多精彩内容