关于MaterialDesign

关于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" />
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值