中文名:材料设计语言
是一套UI设计标准,
材料指的就是控件
什么是Material Design
- 中文名:材料设计语言
是由Google推出的全新的设计语言。
谷歌表示,这种设计语言旨在为手机、平板电脑、台式机和“其他平台” - 是一套UI设计标准;它是android5.0后推出的,本质上来讲多了个Z轴,相当于从2D转换成3D,它不仅可以用在android上,也可以用在网页上。
Material Design能为我们实现什么
- Toolbar
- CollapsingToolbarLayout
- AppBarLayout
- CoordinatorLayout
- DrawerLayout
- NavigationView
- ActionBarDrawerToggle
- Recyclerview
- 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是一个,它主要有两个用途:
- 用作应用的顶层布局管理器,也就是作为用户界面中所有UI控件的容器
- 用作相互之间距有特定交互行为的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();
}
}
...
}
网友评论