Material Design

中文名:材料設(shè)計(jì)語(yǔ)言
是一套UI設(shè)計(jì)標(biāo)準(zhǔn),
材料指的就是控件

什么是Material Design

  1. 中文名:材料設(shè)計(jì)語(yǔ)言
    是由Google推出的全新的設(shè)計(jì)語(yǔ)言。
    谷歌表示,這種設(shè)計(jì)語(yǔ)言旨在為手機(jī)、平板電腦、臺(tái)式機(jī)和“其他平臺(tái)” \color{#0099ff}{提供更一致、更廣泛的外觀和感覺(jué)}
  2. 是一套UI設(shè)計(jì)標(biāo)準(zhǔn);它是android5.0后推出的,本質(zhì)上來(lái)講多了個(gè)Z軸,相當(dāng)于從2D轉(zhuǎn)換成3D,它不僅可以用在android上,也可以用在網(wǎng)頁(yè)上。

Material Design能為我們實(shí)現(xiàn)什么

  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

在系統(tǒng)默認(rèn)生成的顏色中

colorPrimary: APP主要顏色,一般用于ActionBar,ToolBar的背景色設(shè)置。
colorPrimaryDark:比colorPrimary要深一些的顏色,如果狀態(tài)欄顏色android:statusBarColor 沒(méi)有設(shè)置固定值將默認(rèn)為colorPrimaryDark的顏色。
colorAccent: 強(qiáng)調(diào)色,用于如FloatingActionButton小控件上起強(qiáng)調(diào)突出作用的色彩。

AppCompatActivity 是什么

從Android 21之后引入Material Design的設(shè)計(jì)方式,為了支持Material Color 、調(diào)色板、toolbar等各種新特性,AppCompatActivity就應(yīng)用而生。代替了原有的ActionBarActivity。在AppCompatActivity中,更是引入了AppCompatDelegate類的設(shè)計(jì),可以在普通的Acitivity中使用AppCompate的相關(guān)特性。

在平常開(kāi)發(fā)中,Activity繼承的是AppCompatActivity,在這個(gè)類下面,我們?cè)趚ml布局文件中縮寫的控件,都會(huì)被替換成AppCompatXXX控件,以下源碼分析
首先進(jìn)入setContentView方法,實(shí)質(zhì)是delegate的setContentView,delegate是個(gè)抽象類AppCompatDelegate,實(shí)際實(shí)現(xiàn)類是AppCompatDelegateImpl,相當(dāng)于調(diào)用的是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 監(jiān)聽(tīng)xml 的生成過(guò)程,攔截xml 的生成過(guò)程

/**
 * 監(jiān)聽(tīng)xml 的生成過(guò)程,攔截xml 的生成過(guò)程
 */
public interface Factory2 extends Factory {
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
}

AppCompatDelegateImpl在重寫onCreateView中,注釋 1處又返回AppCompatViewInflater的createView,實(shí)際上就通過(guò)名字,創(chuàng)建了一個(gè)view,但是,createTextView又是創(chuàng)建了一個(gè)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,布局中所創(chuàng)建的控件,都是AppCompatxxx,這樣又會(huì)有MaterialDesign的主題,也會(huì)兼容5.0之前

CoordinatorLayout 是什么

CoordinatorLayout是一個(gè)\color{#0099ff}{“加強(qiáng)版”FrameLayout},它主要有兩個(gè)用途:

  1. 用作應(yīng)用的頂層布局管理器,也就是作為用戶界面中所有UI控件的容器
  2. 用作相互之間距有特定交互行為的UI控件的容器;
    通過(guò)為CoordinatorLayout的直接子View指定Behavior,就可以實(shí)現(xiàn)它們之間的交互行為。
    Behavior可以用來(lái)實(shí)現(xiàn)一系列的交互行為和布局變化,比如說(shuō)側(cè)滑菜單、可滑動(dòng)刪除的UI元素,以及跟隨著其他
    UI控件移動(dòng)的按鈕等。

Behavior 是什么

Beahvior是CoordinatorLayout的核心組件,使用Behavior可以實(shí)現(xiàn)CoordinatorLayout內(nèi)直接子控件之間的交互。(PS:直接子控件是指CoordinatorLayout的直接子控件)

Behavior就是一個(gè)觀察者,
重寫B(tài)ehavior,需要重寫2個(gè)參數(shù)的構(gòu)造方法,因?yàn)?/p>

/**
 * @param name 這個(gè)就是xml中寫的名字
 */
static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
    
    if (name.startsWith(".")) {
        // 這個(gè)就是全類名,也就是自己重寫的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);
        }
        // 通過(guò)反射,獲取到自己寫的Behavior
        Constructor<Behavior> c = constructors.get(fullName);
        if (c == null) {
            final Class<Behavior> clazz = (Class<Behavior>) context.getClassLoader()
                    .loadClass(fullName);
            // 這里構(gòu)造方法是2個(gè)參數(shù),所以必須要重寫2個(gè)參數(shù)的構(gòu)造方法
            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
};

/**
 * 在布局的時(shí)候,會(huì)循環(huán)CoordinatorLayout的子控件,如果包含behavior,則進(jìn)行擺放,
 */
@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,則進(jìn)行擺放
        if (behavior == null || !behavior.onLayoutChild(this, child, layoutDirection)) {
            onLayoutChild(child, layoutDirection);
        }
    }
}


/**
 * 通過(guò)攔截,調(diào)用 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()
 * 又通過(guò)lp 獲得了 Behavior 又進(jìn)行了時(shí)間的攔截和 監(jiān)聽(tīng)
 */
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官網(wǎng)
Material Design說(shuō)明簡(jiǎn)書

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容