美文网首页
Material Design

Material Design

作者: 放肆滴微笑 | 来源:发表于2019-11-18 11:30 被阅读0次

    中文名:材料设计语言
    是一套UI设计标准,
    材料指的就是控件

    什么是Material Design

    1. 中文名:材料设计语言
      是由Google推出的全新的设计语言。
      谷歌表示,这种设计语言旨在为手机、平板电脑、台式机和“其他平台” \color{#0099ff}{提供更一致、更广泛的外观和感觉}
    2. 是一套UI设计标准;它是android5.0后推出的,本质上来讲多了个Z轴,相当于从2D转换成3D,它不仅可以用在android上,也可以用在网页上。

    Material Design能为我们实现什么

    1. Toolbar
    2. CollapsingToolbarLayout
    3. AppBarLayout
    4. CoordinatorLayout
    5. DrawerLayout
    6. NavigationView
    7. ActionBarDrawerToggle
    8. Recyclerview
    9. CardView
      等等

    Material Design怎么用

    提供了以下主题

    @android:style/Theme.Material(深色版本)
    @android:style/Theme.Material.Light(浅色版本)
    @android:style/Theme.Material.Light.DarkActionBar
    

    在系统默认生成的颜色中

    colorPrimary: APP主要颜色,一般用于ActionBar,ToolBar的背景色设置。
    colorPrimaryDark:比colorPrimary要深一些的颜色,如果状态栏颜色android:statusBarColor 没有设置固定值将默认为colorPrimaryDark的颜色。
    colorAccent: 强调色,用于如FloatingActionButton小控件上起强调突出作用的色彩。
    

    AppCompatActivity 是什么

    从Android 21之后引入Material Design的设计方式,为了支持Material Color 、调色板、toolbar等各种新特性,AppCompatActivity就应用而生。代替了原有的ActionBarActivity。在AppCompatActivity中,更是引入了AppCompatDelegate类的设计,可以在普通的Acitivity中使用AppCompate的相关特性。

    在平常开发中,Activity继承的是AppCompatActivity,在这个类下面,我们在xml布局文件中缩写的控件,都会被替换成AppCompatXXX控件,以下源码分析
    首先进入setContentView方法,实质是delegate的setContentView,delegate是个抽象类AppCompatDelegate,实际实现类是AppCompatDelegateImpl,相当于调用的是AppCompatDelegateImpl的setContentView

    # AppCompatActivity
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }
    
    public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }
    
    # AppCompatDelegate
    public abstract class AppCompatDelegate{}
    
    # AppCompatDelegateImpl
    
    class AppCompatDelegateImpl extends AppCompatDelegate
            implements MenuBuilder.Callback, LayoutInflater.Factory2 {
        @Override
        public View createView(View parent, final String name, @NonNull Context context,
                               @NonNull AttributeSet attrs) {
            ...
            //注释 1
            return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
                    IS_PRE_LOLLIPOP, /* Only read android:theme pre-L (L+ handles this anyway) */
                    true, /* Read read app:theme as a fallback at all times for legacy reasons */
                    VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the context if enabled */
            );
        }
    }
    
    

    Factory2 监听xml 的生成过程,拦截xml 的生成过程

    /**
     * 监听xml 的生成过程,拦截xml 的生成过程
     */
    public interface Factory2 extends Factory {
        public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
    }
    
    

    AppCompatDelegateImpl在重写onCreateView中,注释 1处又返回AppCompatViewInflater的createView,实际上就通过名字,创建了一个view,但是,createTextView又是创建了一个AppCompatTextView

    # AppCompatViewInflater
    final View createView(View parent, final String name, @NonNull Context context,
                          @NonNull AttributeSet attrs, boolean inheritContext,
                          boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
        ...
        switch (name) {
            case "TextView":
                view = createTextView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "ImageView":
                view = createImageView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "Button":
                view = createButton(context, attrs);
                verifyNotNull(view, name);
                break;
                ...
        }
    }
    
    @NonNull
    protected AppCompatTextView createTextView(Context context, AttributeSet attrs) {
        return new AppCompatTextView(context, attrs);
    }
    

    所以,只要是继承了AppCompatActivity,布局中所创建的控件,都是AppCompatxxx,这样又会有MaterialDesign的主题,也会兼容5.0之前

    CoordinatorLayout 是什么

    CoordinatorLayout是一个\color{#0099ff}{“加强版”FrameLayout},它主要有两个用途:

    1. 用作应用的顶层布局管理器,也就是作为用户界面中所有UI控件的容器
    2. 用作相互之间距有特定交互行为的UI控件的容器;
      通过为CoordinatorLayout的直接子View指定Behavior,就可以实现它们之间的交互行为。
      Behavior可以用来实现一系列的交互行为和布局变化,比如说侧滑菜单、可滑动删除的UI元素,以及跟随着其他
      UI控件移动的按钮等。

    Behavior 是什么

    Beahvior是CoordinatorLayout的核心组件,使用Behavior可以实现CoordinatorLayout内直接子控件之间的交互。(PS:直接子控件是指CoordinatorLayout的直接子控件)

    Behavior就是一个观察者,
    重写Behavior,需要重写2个参数的构造方法,因为

    /**
     * @param name 这个就是xml中写的名字
     */
    static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
        
        if (name.startsWith(".")) {
            // 这个就是全类名,也就是自己重写的Behavior
            fullName = context.getPackageName() + name;
        } else if (name.indexOf('.') >= 0) {
            fullName = name;
        } else {
            fullName = !TextUtils.isEmpty(WIDGET_PACKAGE_NAME)
                    ? (WIDGET_PACKAGE_NAME + '.' + name)
                    : name;
        }
    
        try {
            Map<String, Constructor<Behavior>> constructors = sConstructors.get();
            if (constructors == null) {
                constructors = new HashMap<>();
                sConstructors.set(constructors);
            }
            // 通过反射,获取到自己写的Behavior
            Constructor<Behavior> c = constructors.get(fullName);
            if (c == null) {
                final Class<Behavior> clazz = (Class<Behavior>) context.getClassLoader()
                        .loadClass(fullName);
                // 这里构造方法是2个参数,所以必须要重写2个参数的构造方法
                c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
                c.setAccessible(true);
                constructors.put(fullName, c);
            }
            return c.newInstance(context, attrs);
        } catch (Exception e) {
            throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
        }
    }
    
    static final Class<?>[] CONSTRUCTOR_PARAMS = new Class<?>[] {
            Context.class,
            AttributeSet.class
    };
    
    /**
     * 在布局的时候,会循环CoordinatorLayout的子控件,如果包含behavior,则进行摆放,
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int layoutDirection = ViewCompat.getLayoutDirection(this);
        final int childCount = mDependencySortedChildren.size();
        for (int i = 0; i < childCount; i++) {
            final CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
            final Behavior behavior = lp.getBehavior();
            // 如果包含behavior,则进行摆放
            if (behavior == null || !behavior.onLayoutChild(this, child, layoutDirection)) {
                onLayoutChild(child, layoutDirection);
            }
        }
    }
    
    
    /**
     * 通过拦截,调用 resetTouchBehaviors
     */
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getActionMasked();
    
        // Make sure we reset in case we had missed a previous important event.
        if (action == MotionEvent.ACTION_DOWN) {
            resetTouchBehaviors(true);
        }
    
        final boolean intercepted = performIntercept(ev, TYPE_ON_INTERCEPT);
    
        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
            resetTouchBehaviors(true);
        }
    
        return intercepted;
    }
    
    /**
     * 获取到child.getLayoutParams()
     * 又通过lp 获得了 Behavior 又进行了时间的拦截和 监听
     */
    private void resetTouchBehaviors(boolean notifyOnInterceptTouchEvent) {
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            final CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
            final Behavior b = lp.getBehavior();
            if (b != null) {
                final long now = SystemClock.uptimeMillis();
                final MotionEvent cancelEvent = MotionEvent.obtain(now, now,
                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
                if (notifyOnInterceptTouchEvent) {
                    b.onInterceptTouchEvent(this, child, cancelEvent);
                } else {
                    b.onTouchEvent(this, child, cancelEvent);
                }
                cancelEvent.recycle();
            }
        }
        ...
    }
    

    参考资料

    Material Design官网
    Material Design说明简书

    相关文章

      网友评论

          本文标题:Material Design

          本文链接:https://www.haomeiwen.com/subject/tywyictx.html