Android5.0 Material design - NestedScrollingParent 和 NestedScrollingChild

        在界面开发过程中,我们经常遇到子控件的移动到某一阶段,引起父控件或者其他控件产生动态效果;

        在android 5.0 的Material design设计中,就为我们封装了一套这样的接口,即 NestedScrollingParent 和 NestedScrollingChild;

原理比较简单,当子控件需要滑动的时候,即调用startNestedScroll方法,在滑动之前,可以调用dispatchNestedPreScroll方法,来告诉父view是否需要滑动;
那么子view和父view是如果做到沟通的尼,这就需要第三个类来帮忙了 —- NestedScrollingChildHelper

下面的代码实现了 向左边滑动文字,当文字超过边界的时候, 图片收缩
这里写图片描述

package com.migu.hwj.component;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.view.NestedScrollingChild;
import android.support.v4.view.NestedScrollingChildHelper;
import android.support.v4.view.NestedScrollingParent;
import android.support.v4.view.NestedScrollingParentHelper;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.migu.hwj.as.leanring.R;
import com.migu.hwj.util.MyLog;

/*
 *  演示Android5.0中Material design设计总新增的一些控件和Api
 *
 *  同时这个类也演示了在XML中如何使用内部类,即在xml中使用内部类有两个注意的地方
 *  1: 内部类是static的,public private没关系
 *  2:在xml中控件需要使用view这样的标签,并以class标签描述类的完整路径,以$链接内部类
 *
 *  参考:http://blog.csdn.net/gorgle/article/details/51428515
 */

/*
 * NestedScrolling机制
 * 作用: 让父View和子View在滚动式进行配合,实现诸如子view滑动一定距离,父view是如何滑动的效果等等
 * 主要接口
 *     NestedScrollingChild
 *     NestedScrollingParent
 * 帮助类
 *   NestedScrollingChildHelper
 *   NestedScrollingParentHelper
 *
 * 子view一般继承NestedScrollingChild接口作为动作的发起者, 父view一般继承NestedScrollingParent作用动作的响应者
 * 正因为这种关系,因此当继承NestedScrollingChild接口的子view一些方法触发的时候,继承NestedScrollingParent的父view的一些方法也会被回调
 *
 * 例如:
 * 子view的startNestedScroll会触发父view的onStartNestedScroll
 *
 */

public class KitkatActivity extends Activity {

    private KitKatNestedScrollingParent mKitKatNestedScrollingParent;
    private LinearLayout.LayoutParams mKitNetedParentParams;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_nested_scrolling);
    }


    /*
     * Android5.0中Material design设计中配合子view滑动的父控件
     */
    public static class KitKatNestedScrollingParent extends LinearLayout implements NestedScrollingParent {

        private NestedScrollingParentHelper mNestedScrollingParentHelper;

        public KitKatNestedScrollingParent(Context context) {
            super(context);
            init();
        }

        public KitKatNestedScrollingParent(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }


        private void init() {
            mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
        }


        //因为实现NestedScrollingParent接口,而实现的方法

        /**
         * 回调开始滑动  该方法决定了当前控件是否能接收到其内部View滑动时的参数
         *
         * @param child            该父VIew 的子View
         * @param target           支持嵌套滑动的 VIew
         * @param nestedScrollAxes 滑动方向
         * @return 是否支持 嵌套滑动
         */
        @Override
        public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
            MyLog.logD("KitKatNestedScrollingParent onStartNestedScroll");
            if (target instanceof KitKatNestedScrollingChild) {
                return true;
            }
            return false;
        }

        //因为实现NestedScrollingParent接口,而实现的方法
        @Override
        public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
            MyLog.logD("KitKatNestedScrollingParent onNestedScrollAccepted");
            mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
        }

        //因为实现NestedScrollingParent接口,而实现的方法
        @Override
        public void onStopNestedScroll(View target) {
            MyLog.logD("KitKatNestedScrollingParent onStopNestedScroll");
            mNestedScrollingParentHelper.onStopNestedScroll(target);
        }

        //因为实现NestedScrollingParent接口,而实现的方法
        @Override
        public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
            MyLog.logD("KitKatNestedScrollingParent onNestedScroll");
        }

        //因为实现NestedScrollingParent接口,而实现的方法
        /*
         * 该方法的会传入内部View移动的dx,dy 前3个为输入参数,最后一个是输出参数
         * consumed : The horizontal and vertical scroll distance consumed by this parent
         * 向上滑动 dy > 0, dx应该等于0(如果X坐标没有发生变化)
         * 向下滑动 dy < 0, dx应该等于0(如果X坐标没有发生变化)
         * 向左滑动 dx > 0, dy应该等于0(如果y坐标没有发生变化)
         * 向右滑动 dx < 0, dy应该等于0(如果y坐标没有发生变化)
         *
         * @param target View that initiated the nested scroll
         * @param dx Horizontal scroll distance in pixels
         * @param dy Vertical scroll distance in pixels
         * @param consumed Output. The horizontal and vertical scroll distance consumed by this parent
         */
        @Override
        public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
            MyLog.logD("KitKatNestedScrollingParent onNestedPreScroll dx " + dx);
            if (dx > 0) {
                scrollBy(dx,0);//滚动
                consumed[0] = dx;//告诉child我消费了多少
            }
        }

        //因为实现NestedScrollingParent接口,而实现的方法
        /*
         * 捕获对内部View的fling事件,如果return true则表示拦截掉内部View的事件
         */
        @Override
        public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
            MyLog.logD("KitKatNestedScrollingParent onNestedFling");
            return false;
        }

        //因为实现NestedScrollingParent接口,而实现的方法
        @Override
        public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
            MyLog.logD("KitKatNestedScrollingParent onNestedPreFling");
            return false;
        }

        //因为实现NestedScrollingParent接口,而实现的方法
        @Override
        public int getNestedScrollAxes() {
            MyLog.logD("KitKatNestedScrollingParent getNestedScrollAxes");
            return mNestedScrollingParentHelper.getNestedScrollAxes();
        }

        //scrollBy内部会调用scrollTo
        @Override
        public void scrollTo(int x, int y) {
            super.scrollTo(x, y);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }

        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            super.onLayout(changed, l, t, r, b);
        }
    }

    /*
     * Android5.0中Material design设计中子view控件
     */
    public static class KitKatNestedScrollingChild extends LinearLayout implements NestedScrollingChild {

        //NestedScrolling帮助类,这个类似ontouch里面使用了手势识别帮助类
        private NestedScrollingChildHelper mNestedScrollingChildHelper;

        //上一次水平滑动距离
        private int lastX;

        private final int[] offset = new int[2];   //偏移量
        private final int[] consumed = new int[2]; //消费

        //子view宽
        private int childWidth;

        public KitKatNestedScrollingChild(Context context) {
            super(context);
        }

        public KitKatNestedScrollingChild(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        /**
         * 设置嵌套滑动是否可用
         *
         * @param enabled 参数enabled:true表示view使用嵌套滚动,false表示禁用.
         */
        @Override
        public void setNestedScrollingEnabled(boolean enabled) {
            MyLog.logD("KitKatNestedScrollingChild getNestedScrollAxes");
            getScrollingChildHelper().setNestedScrollingEnabled(enabled);
        }

        /**
         * 嵌套滑动是否可用
         *
         * @return
         */
        @Override
        public boolean isNestedScrollingEnabled() {
            MyLog.logD("KitKatNestedScrollingChild isNestedScrollingEnabled");
            return getScrollingChildHelper().isNestedScrollingEnabled();
        }

        /**
         * 开始嵌套滑动,
         *
         * @param axes 表示方向 有一下两种值
         *             ViewCompat.SCROLL_AXIS_HORIZONTAL 横向哈东
         *             ViewCompat.SCROLL_AXIS_VERTICAL 纵向滑动
         *             <p>
         *             返回值:true表示本次滚动支持嵌套滚动,false不支持
         */
        @Override
        public boolean startNestedScroll(int axes) {
            MyLog.logD("KitKatNestedScrollingChild startNestedScroll");
            return getScrollingChildHelper().startNestedScroll(axes);
        }

        @Override
        public void stopNestedScroll() {
            MyLog.logD("KitKatNestedScrollingChild stopNestedScroll");
            getScrollingChildHelper().stopNestedScroll();
        }

        /**
         * 是否有父View 支持 嵌套滑动,  会一层层的网上寻找父View
         *
         * @return
         */
        @Override
        public boolean hasNestedScrollingParent() {
            MyLog.logD("KitKatNestedScrollingChild hasNestedScrollingParent");
            return getScrollingChildHelper().hasNestedScrollingParent();
        }

        /**
         * 在处理滑动之后 调用
         *
         * @param dxConsumed     表示view消费了x方向的距离长度
         * @param dyConsumed     表示view消费了y方向的距离长度
         * @param dxUnconsumed   表示滚动产生的x滚动距离还剩下多少没有消费
         * @param dyUnconsumed   表示滚动产生的y滚动距离还剩下多少没有消费
         * @param offsetInWindow 表示剩下的距离dxUnconsumed和dyUnconsumed使得view在父布局中的位置偏移了多少
         * @return
         */

        @Override
        public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
            MyLog.logD("KitKatNestedScrollingChild dispatchNestedScroll");
            return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
        }

        /**
         * 一般在滑动之前调用, 在ontouch 中计算出滑动距离, 然后调用该方法, 就给支持的嵌套的父View 处理滑动事件
         *
         * @param dx             x 轴上滑动的距离, 相对于上一次事件, 不是相对于 down事件的 那个距离
         * @param dy             y 轴上滑动的距离
         * @param consumed       表示父布局消费的距离 一个数组, 可以传 一个空的 数组,  表示 x 方向 或 y 方向的事件 是否有被消费  consumed[0]表示x方向,consumed[1]表示y方向
         * @param offsetInWindow 支持嵌套滑动到额父View 消费 滑动事件后 导致 本 View 的移动距离
         * @return 支持的嵌套的父View 是否处理了 滑动事件
         */
        @Override
        public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
            MyLog.logD("KitKatNestedScrollingChild dispatchNestedPreScroll");
            return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
        }

        /**
         * @param velocityX x 轴上的滑动速度
         * @param velocityY y 轴上的滑动速度
         * @param consumed  是否被消费
         * @return
         */
        @Override
        public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
            MyLog.logD("KitKatNestedScrollingChild dispatchNestedFling");
            return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
        }

        /**
         * @param velocityX x 轴上的滑动速度
         * @param velocityY y 轴上的滑动速度
         * @return
         */
        @Override
        public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
            MyLog.logD("KitKatNestedScrollingChild dispatchNestedPreFling");
            return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            childWidth = this.getMeasuredWidth();
            MyLog.logD("KitKatNestedScrollingChild onMeasure childWidth " + childWidth);
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {

            switch (event.getAction()) {
                //按下
                case MotionEvent.ACTION_DOWN:
                    lastX = (int) event.getRawX();
                    break;
                //移动
                case MotionEvent.ACTION_MOVE:
                    int x = (int) (event.getRawX());
                    int dx = x - lastX;
                    lastX = x;
                    scrollBy(-dx, 0);
                    break;
            }
            return true;
        }

        //限制滚动范围
        @Override
        public void scrollTo(int x, int y) {
            super.scrollTo(x, y);
            //当超出内容显示的时候,通知父控制收放imageview
            MyLog.logD("KitKatNestedScrollingChild scrollTo  x " + x  + " lastX  " + lastX);
            //向左滑
            if(x >= childWidth && x > 0){
                //startNestedScroll开始滑动  dispatchNestedPreScroll 通知父控件滑动
                startNestedScroll(ViewCompat.SCROLL_AXIS_HORIZONTAL);
                dispatchNestedPreScroll(lastX, 0, consumed, offset);
            }
        }

        //初始化helper对象
        private NestedScrollingChildHelper getScrollingChildHelper() {
            if (mNestedScrollingChildHelper == null) {
                mNestedScrollingChildHelper = new NestedScrollingChildHelper(this);
                mNestedScrollingChildHelper.setNestedScrollingEnabled(true);
            }
            return mNestedScrollingChildHelper;
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:orientation="horizontal"
              android:layout_height="match_parent"
    >

    <view
        class="com.migu.hwj.component.KitkatActivity$KitKatNestedScrollingParent"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:orientation="horizontal">h

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:src="@mipmap/ic_launcher"/>

        <view
            class="com.migu.hwj.component.KitkatActivity$KitKatNestedScrollingChild"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:text="向左滑动可以收缩左边的图片"
                android:gravity="center"
                android:textColor="#f0f"
                android:textSize="20sp"/>
        </view>
        -->
    </view>
</LinearLayout>
package com.migu.hwj.util;

import android.util.Log;


public class MyLog{

    private static boolean mOpenLog = true;
    private static String mTag = "hwj";

    public static void setTag(String tag){
        mTag = tag;
    }

    public static void logD(String message){
        if(mOpenLog){
            Log.d(mTag,message);
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值