Android知识积累

EffectiveAndroidUI 概述

2016-04-05  本文已影响877人  北疆_

邂逅

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

观后感

代码走读

项目使用三种布局来分别适配了低版本(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();
      }
    }
  }
上一篇 下一篇

猜你喜欢

热点阅读