关于MaterialDesign
主题
api>21
android:style/Theme.Material
android:style/Theme.Material.Light
android:style/Theme.Material.Light.DarkActionBar
兼容主题api<21
Theme.AppCompat.Light
Theme.AppCompat.Light.DarkActionBar
主题常用属性
colorPrimary 标题栏颜色
colorPrimaryDark 状态栏颜色
colorAccent 强调色
textColorPrimary 标题栏字体颜色
windowBackground 窗口背景色
navigationBarColor 虚拟导航栏色
主题设置:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<!-- 窗口颜色 -->
<item name="android:windowBackground">@android:color/white</item>
<!-- 虚拟导航栏颜色 -->
<item name="android:navigationBarColor">@color/colorPrimary</item>
<!--设置menu菜单的背景色-->
<!--<item name="android:itemBackground">@color/black</item>-->
<!--设置Menu菜单的字体颜色-->
<!--<item name="android:textColorPrimary">@android:color/darker_gray</item>-->
<!--设置Menu窗口不覆盖ToolBar视图(下方显示)-->
<item name="overlapAnchor">false</item>
</style>
常用控件:
Toolbar
<android.support.v7.widget.Toolbar
android:id="@+id/tb_at_toolbar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" //菜单弹出效果
app:title="title"
app:subtitle="subtitle"
app:titleTextColor="@android:color/white"
app:titleTextAppearance="@style/ToolBar.TitleText"
app:subtitleTextColor="@android:color/white">
<!--app:logo //图标-->
<!-- 将toolbar的title移动到中间-->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="title"
app:titleTextColor="@android:color/white" />
</android.support.v7.widget.Toolbar>
DrawerLayout:左拉右拉菜单
NavigationView(与DrawerLayout结合 侧滑菜单)
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity"
tools:openDrawer="start">
<include
layout="@layout/main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- 侧滑菜单-->
<android.support.design.widget.NavigationView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/header" //头部layout布局
app:menu="@menu/drawer" //侧滑菜单
/>
</android.support.v4.widget.DrawerLayout>
bottomNavigationView(底部导航栏)
<android.support.design.widget.BottomNavigationView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/navigation_bottom" /> //导航栏菜单
bottomNavigationView监听
navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.home:
mTextMessage.setText("home");
return true;
case R.id.recent:
mTextMessage.setText("recent");
return true;
case R.id.all:
mTextMessage.setText("all");
return true;
}
return false;
}
});
FloatingActionButton 浮动按钮
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:elevation="8dp"
android:src="@drawable/add" />
<!-- android:elevation一个阴影的作用、MD设计的高度Z-->
Snackbar 提示功能
Snackbar.make(view, "snack ", 1000)
.setAction("Toast", new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, " toast ", Toast.LENGTH_SHORT).show();
}
}).show();
CardView
app:cardBackgroundColor="@color/white" //背景色
app:cardCornerRadius="8dp" //设置cardView圆角效果
app:cardElevation="2dp" //设置cardView Z轴阴影大小
app:cardMaxElevation="8dp" //设置cardView Z轴最大阴影
app:contentPadding="8dp" //设置内容的内边距
//为了兼容一般加上以下两个属性
app:cardPreventCornerOverlap="false" //取消Lollipop以下版本的padding
app:cardUseCompatPadding="true" //为 Lollipop 及其以上版本增加一个阴影padding内边距
CoordinatorLayout:
继承自ViewGroup;使用相当于Framlayout;为子view指定behavior,实现自定义交互
Behavior设置
CoordinatorLayout 内部类LayoutParams
LayoutParams(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, styleable.CoordinatorLayout_Layout);
this.gravity = a.getInteger(styleable.CoordinatorLayout_Layout_android_layout_gravity, 0);
this.mAnchorId = a.getResourceId(styleable.CoordinatorLayout_Layout_layout_anchor, -1);
this.anchorGravity = a.getInteger(styleable.CoordinatorLayout_Layout_layout_anchorGravity, 0);
this.keyline = a.getInteger(styleable.CoordinatorLayout_Layout_layout_keyline, -1);
this.insetEdge = a.getInt(styleable.CoordinatorLayout_Layout_layout_insetEdge, 0);
this.dodgeInsetEdges = a.getInt(styleable.CoordinatorLayout_Layout_layout_dodgeInsetEdges, 0);
this.mBehaviorResolved = a.hasValue(styleable.CoordinatorLayout_Layout_layout_behavior);
if (this.mBehaviorResolved) {
this.mBehavior = CoordinatorLayout.parseBehavior(context, attrs, a.getString(styleable.CoordinatorLayout_Layout_layout_behavior));
}
a.recycle();
if (this.mBehavior != null) {
this.mBehavior.onAttachedToLayoutParams(this);
}
}
其中,如果需要解析behavior,调用parseBehavior。
if (this.mBehaviorResolved) {
this.mBehavior = CoordinatorLayout.parseBehavior(context, attrs, a.getString(styleable.CoordinatorLayout_Layout_layout_behavior));
}
在parseBehavior方法中对behavior反射实例化(两参构造函数)
Constructor<CoordinatorLayout.Behavior> c = (Constructor)((Map)constructors).get(fullName);
if (c == null) {
Class<CoordinatorLayout.Behavior> clazz = context.getClassLoader().loadClass(fullName);
c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
c.setAccessible(true);
((Map)constructors).put(fullName, c);
}`
CONSTRUCTOR_PARAMS = new Class[]{Context.class, AttributeSet.class};
使用LayoutParams中 getBehavior()获取实例化的Behavior。
public CoordinatorLayout.Behavior getBehavior() {
return this.mBehavior;
}
使用LayoutParams中 gsetBehavior(@Nullable CoordinatorLayout.Behavior behavior) 设置Behavior。
结合Behavior实现对子view的事件相应
CoordinatorLayout采用内嵌滑动机制。内嵌滑动机制:提供父view和子view嵌套滑动的交互机制,父view需要实现NestedScrollingParent接口,子view实现NestedScrollingChild接口。
RecyclerView滑动 down事件中:
public boolean onInterceptTouchEvent(MotionEvent e) {
...
this.startNestedScroll(nestedScrollAxis, 0);
....
}
public boolean startNestedScroll(int axes, int type) {
return this.getScrollingChildHelper().startNestedScroll(axes, type);
}
调用NestedScrollingChildHelper中
public boolean startNestedScroll(int axes, int type) {
if (this.hasNestedScrollingParent(type)) {
return true;
} else {
if (this.isNestedScrollingEnabled()) {//是否支持内嵌滑动
ViewParent p = this.mView.getParent();
for(View child = this.mView; p != null; p = p.getParent()) {
if (ViewParentCompat.onStartNestedScroll(p, child, this.mView, axes, type)) {
this.setNestedScrollingParentForType(type, p);
ViewParentCompat.onNestedScrollAccepted(p, child, this.mView, axes, type);
return true;
}
if (p instanceof View) {
child = (View)p;
}
}
}
return false;
}
}
ViewParentCompat中onStartNestedScroll:
public static boolean onStartNestedScroll(ViewParent parent, View child, View target, int nestedScrollAxes, int type) {
if (parent instanceof NestedScrollingParent2) { //CoordinatorLayout
return ((NestedScrollingParent2)parent).onStartNestedScroll(child, target, nestedScrollAxes, type);
} else {
if (type == 0) {
if (VERSION.SDK_INT >= 21) {
try {
return parent.onStartNestedScroll(child, target, nestedScrollAxes);
} catch (AbstractMethodError var6) {
Log.e("ViewParentCompat", "ViewParent " + parent + " does not implement interface " + "method onStartNestedScroll", var6);
}
} else if (parent instanceof NestedScrollingParent) {
return ((NestedScrollingParent)parent).onStartNestedScroll(child, target, nestedScrollAxes);
}
}
return false;
}
}
调用CoordinatorLayout中onStartNestedScroll(child, target, nestedScrollAxes, type)方法
public boolean onStartNestedScroll(View child, View target, int axes, int type) {
boolean handled = false;
int childCount = this.getChildCount();
for(int i = 0; i < childCount; ++i) {
View view = this.getChildAt(i);
if (view.getVisibility() != 8) {
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)view.getLayoutParams();
CoordinatorLayout.Behavior viewBehavior = lp.getBehavior();//子view的behavior
if (viewBehavior != null) {
boolean accepted = viewBehavior.onStartNestedScroll(this, view, child, target, axes, type);
handled |= accepted;
lp.setNestedScrollAccepted(type, accepted);
} else {
lp.setNestedScrollAccepted(type, false);
}
}
}
return handled;
}
实现子view Behavior的viewBehavior.onStartNestedScroll(this, view, child, target, axes, type)方法。
RecyclerView滑动 move事件中:
public boolean onTouchEvent(MotionEvent e) {
....
case 2:
if (this.scrollByInternal(canScrollHorizontally ? dx : 0, canScrollVertically ? dy : 0, vtev)) {
this.getParent().requestDisallowInterceptTouchEvent(true);
}
break;
...
}
boolean scrollByInternal(int x, int y, MotionEvent ev) {
...
if (this.dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, this.mScrollOffset, 0)) {
...
}
...
}
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow, int type) {
return this.getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow, type);
}
NestedScrollingChildHelper中:
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow, int type) {
if (this.isNestedScrollingEnabled()) {
...
ViewParentCompat.onNestedScroll(parent, this.mView, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
...
}
ViewParentCompat中:
public static void onNestedScroll(ViewParent parent, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
if (parent instanceof NestedScrollingParent2) {
((NestedScrollingParent2)parent).onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
} else if (type == 0) {
if (VERSION.SDK_INT >= 21) {
try {
parent.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
} catch (AbstractMethodError var8) {
Log.e("ViewParentCompat", "ViewParent " + parent + " does not implement interface " + "method onNestedScroll", var8);
}
} else if (parent instanceof NestedScrollingParent) {
((NestedScrollingParent)parent).onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
}
}
调用CoordinatorLayout中onNestedScroll方法
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
int childCount = this.getChildCount();
boolean accepted = false;
for(int i = 0; i < childCount; ++i) {
View view = this.getChildAt(i);
if (view.getVisibility() != 8) {
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)view.getLayoutParams();
if (lp.isNestedScrollAccepted(type)) {
CoordinatorLayout.Behavior viewBehavior = lp.getBehavior();
if (viewBehavior != null) {
viewBehavior.onNestedScroll(this, view, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
accepted = true;
}
}
}
}
if (accepted) {
this.onChildViewsChanged(1);
}
}
实现子view Behavior的viewBehavior.onNestedScroll方法。
Behavior中
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) {
return nestedScrollAxes == 2;
}
返回true,能够接收后续事件,返回false不接收后续事件。
AppBarLayout:
垂直线性布局
响应CoordinatorLayout中的behavior属性,一般结合CoordinatorLayout使用
AppBarLayout的直接子控件可以设置的属性:layout_scrollFlags(滚动模式)
scroll|exitUntilCollapsed:该子控件可以滚动,向上滚动NestedScrollView出父布局(一般为CoordinatorLayout)时,会折叠到顶端,向下滚动时NestedScrollView必须滚动到最上面的时候才能拉出该布局
scroll|enterAlways:只要向下滚动该布局就会显示出来,只要向上滑动该布局就会向上收缩
scroll|enterAlwaysCollapsed:向下滚动NestedScrollView到最底端时该布局才会显示出来
scroll|snap:表示一个吸附效果,可以确保childView不会滑动停止在中间的状态
scroll:如果不设置该属性,则该布局不能滑动
app:contentScrim="?attr/colorPrimary"指定折叠后背景色`
CollapsingToolbarLayout:
可折叠自己的布局,一般结合CoordinatorLayout使用
app:layout_collapseMode="pin"
parallax:视差滚动子View
pin:将子View位置固定
NestedScrollView:
嵌套滑动,使用方式与ScrollView一致
常用动画:
Fade淡入
getWindow().setEnterTransition(new Fade());
Slide滑动 (底部弹出)
getWindow().setEnterTransition(new Slide());
Explode分解
Intent intent = new Intent(this, ExplodeActivity.class);
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
ExplodeActivity:
getWindow().setEnterTransition(new Explode());
共享元素
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(
MainActivity.this,view.findViewById(R.id.icon),"basic" );
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
startActivity(intent, optionsCompat.toBundle());
DetailActivity布局:
<ImageView
android:id="@+id/detail_icon"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/icon"
android:transitionName="basic" />