Android开发——自定义view之京东淘宝首页二级联动

Android开发——自定义view之京东淘宝首页二级联动,第1张

概述1、布局 2、解决问题问题1:嵌套滑动冲突我们知道,通过上面的简单布局,在执行代码的时候,会有滑动冲突产生,当我们滑动recyclerView的时候,headerView不会跟着滑动。解决办法:嵌套滑动是需要两个角色的,一个是父亲(ScrollView),一个是孩子(recyclerView),我们的recyclerView实现了NestedSc 1、布局

 

2、解决问题问题1:嵌套滑动冲突

我们知道,通过上面的简单布局,在执行代码的时候,会有滑动冲突产生,当我们滑动recyclerVIEw的时候,headerVIEw不会跟着滑动。

解决办法:嵌套滑动是需要两个角色的,一个是父亲(ScrollVIEw),一个是孩子(recyclerVIEw),我们的recyclerVIEw实现了nestedScrollingChild,而ScrollVIEw并没有实现nestedScrollingParent,因此ScrollVIEw不能作为父亲,而我们的界面用到的是嵌套滑动,嵌套滑动又必须要有父亲,因此我们将ScrollVIEw改成nestedScrollVIEw即可,nestedScrollVIEw实现了nestedScrollingParent接口。此时事件已经被传递,但是没有吸顶。

原因:事件分发原理

 

问题2:TabLayout吸顶效果

处理方式:

固定位置事件拦截备胎(弄两个tabLayout,进行高度计算隐藏和显示)

我们这里用固定位置来做。思路如下:

先看图

我们将nestedScrollVIEw的高度固定,高度等于headervIEw+屏幕的高度,而屏幕的高度=tablayout的高度+vIEwpager的高度

当nestedScrollVIEw滑动到底部的时候,也就是headervIEw全部隐藏的时候,此时tabLayout在屏幕最顶部,nestedScrollVIEw已经滑动到了底部,我们继续滑动屏幕的时候,实际上是在滑动recyclerVIEw,这样tabLayout就会有一个吸顶的效果。

<?xml version="1.0" enCoding="utf-8"?><layout>    <androIDx.swiperefreshlayout.Widget.SwipeRefreshLayout xmlns:androID="http://schemas.androID.com/apk/res/androID"        androID:layout_wIDth="match_parent"        androID:ID="@+ID/swipe_refresh_layout"        androID:layout_height="match_parent">        <com.nestedscroll.e_perfect_nestedscroll.nestedScrollLayout            androID:layout_wIDth="match_parent"            androID:layout_height="match_parent"            androID:orIEntation="vertical">            <linearLayout                androID:layout_wIDth="match_parent"                androID:layout_height="match_parent"                androID:orIEntation="vertical">                <com.common.vIEws.xxrecyclervIEw.FixedDataScrollDisabledRecyclerVIEw                    androID:ID="@+ID/combo_top_vIEw"                    androID:layout_wIDth="match_parent"                    androID:layout_height="wrap_content" />                <linearLayout                    androID:layout_wIDth="match_parent"                    androID:layout_height="match_parent"                    androID:orIEntation="vertical">                    <com.Google.androID.material.tabs.TabLayout                        androID:ID="@+ID/tablayout"                        androID:layout_wIDth="match_parent"                        androID:layout_height="wrap_content" />                    <androIDx.vIEwpager2.Widget.VIEwPager2                        androID:ID="@+ID/vIEwpager_vIEw"                        androID:layout_wIDth="match_parent"                        androID:layout_height="match_parent" />                </linearLayout>            </linearLayout>        </com.nestedscroll.e_perfect_nestedscroll.nestedScrollLayout>    </androIDx.swiperefreshlayout.Widget.SwipeRefreshLayout></layout>

 

我们需要自定义一个nestedScrollVIEw,然后重写里面的两个方法

  OverrIDe    protected voID onFinishInflate() {        super.onFinishInflate();        contentVIEw = (VIEwGroup) ((VIEwGroup) getChildAt(0)).getChildAt(1);    }    @OverrIDe    protected voID onMeasure(int wIDthMeasureSpec, int heightmeasureSpec) {        // 调整contentVIEw的高度为父容器高度,使之填充布局,避免父容器滚动后出现空白        super.onMeasure(wIDthMeasureSpec, heightmeasureSpec);        VIEwGroup.LayoutParams lp = contentVIEw.getLayoutParams();        lp.height = getMeasuredHeight();        contentVIEw.setLayoutParams(lp);    }

当加载完毕之后,获取到nestedScrollVIEw下第0个元素的第1个元素,也就是这段

            <linearLayout                    androID:layout_wIDth="match_parent"                    androID:layout_height="match_parent"                    androID:orIEntation="vertical">                    <com.Google.androID.material.tabs.TabLayout                        androID:ID="@+ID/tablayout"                        androID:layout_wIDth="match_parent"                        androID:layout_height="wrap_content" />                    <androIDx.vIEwpager2.Widget.VIEwPager2                        androID:ID="@+ID/vIEwpager_vIEw"                        androID:layout_wIDth="match_parent"                        androID:layout_height="match_parent" />                </linearLayout>    

然后通过onMeasure计算高度。

此时吸顶效果完成。吸顶其实是假象,实际上是nestedScrollVIEw已经到头了,继续滑动屏幕的话实际上滑动的是recyclerVIEw。

 

问题3:nestedScrollVIEw和recyclerVIEw的滑动没有连接起来

还是回到前面,嵌套滑动是由父亲和孩子两个元素来完成的,所以我们这里需要用子vIEw来主动触发,也就是recyclerVIEw主动触发,nestedScrollVIEw跟随滑动。

因此当nestedScrollVIEw还未处于底部的时候,也就是说nestedScrollVIEw还可以继续滑动的时候,我们需要对滑动进行拦截,告诉recyclerVIEw,父亲滑动就行了,你不用动。

所以我们重写拦截方法onnestedPreScroll,如果不拦截,那么会交给nestedScrollVIEw的父亲去执行,并不是我们想要的

  @OverrIDe    protected voID onFinishInflate() {        super.onFinishInflate();        topVIEw = ((VIEwGroup) getChildAt(0)).getChildAt(0);        contentVIEw = (VIEwGroup) ((VIEwGroup) getChildAt(0)).getChildAt(1);    }    @OverrIDe    protected voID onMeasure(int wIDthMeasureSpec, int heightmeasureSpec) {        // 调整contentVIEw的高度为父容器高度,使之填充布局,避免父容器滚动后出现空白        super.onMeasure(wIDthMeasureSpec, heightmeasureSpec);        VIEwGroup.LayoutParams lp = contentVIEw.getLayoutParams();        lp.height = getMeasuredHeight();        contentVIEw.setLayoutParams(lp);    }    @OverrIDe    public voID onnestedPreScroll(@NonNull VIEw target, int dx, int dy, @NonNull int[] consumed, int type) {        Log.i("nestedScrollLayout", getScrollY()+"::onnestedPreScroll::"+topVIEw.getMeasuredHeight());        // 向上滑动。若当前topvIEw可见,需要将topvIEw滑动至不可见        boolean hIDetop = dy > 0 && getScrollY() < topVIEw.getMeasuredHeight();        if (hIDetop) {            scrollBy(0, dy);            consumed[1] = dy;        }    }

 

问题4:惯性滑动

思路

记下速度转成距离父vIEw滑动了多少距离计算子vIEw该滑动的距离(根据速度转换后的距离-父vIEw滑动了的额距离=子vIEw该滑的距离)子vIEw该滑的距离转成速度(因为recyclerVIEw只有一个fling方法,该方法接收的是一个速度值,因此我们要把剩余的距离转成速度传进去,而不是直接传距离)

下面直接贴代码,代码有注释,配合上面的思路应该就清楚了。

public class nestedScrollLayout extends nestedScrollVIEw {    private VIEw topVIEw;    private VIEwGroup contentVIEw;    private static final String TAG = "nestedScrollLayout";    public nestedScrollLayout(Context context) {        this(context, null);        init();    }    public nestedScrollLayout(Context context, @Nullable AttributeSet attrs) {        this(context, attrs, 0);        init();    }    public nestedScrollLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        this(context, attrs, defStyleAttr, 0);        init();    }    public nestedScrollLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr);        init();    }    private FlingHelper mFlingHelper;    int totalDy = 0;    /**     * 用于判断RecyclerVIEw是否在fling     */    boolean isstartFling = false;    /**     * 记录当前滑动的y轴加速度     */    private int veLocityY = 0;    private voID init() {        mFlingHelper = new FlingHelper(getContext());        setonScrollchangelistener(new VIEw.OnScrollchangelistener() {            @OverrIDe            public voID onScrollChange(VIEw v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {                if (isstartFling) {                    totalDy = 0;                    isstartFling = false;                }                if (scrollY == 0) {                    Log.i(TAG, "top SCRolL");                   // refreshLayout.setEnabled(true);                }                if (scrollY == (getChildAt(0).getMeasuredHeight() - v.getMeasuredHeight())) {                    Log.i(TAG, "BottOM SCRolL");                    dispatchChildFling();                }                //在RecyclerVIEw fling情况下,记录当前RecyclerVIEw在y轴的偏移                totalDy += scrollY - oldScrollY;            }        });    }
  //获取剩余距离 private voID dispatchChildFling() { if (veLocityY != 0) { Double splineFlingdistance = mFlingHelper.getSplineFlingdistance(veLocityY); if (splineFlingdistance > totalDy) { childFling(mFlingHelper.getVeLocityBydistance(splineFlingdistance - Double.valueOf(totalDy))); } } totalDy = 0; veLocityY = 0; }
  //计算子vIEw滑动的距离 private voID childFling(int velY) { RecyclerVIEw childRecyclerVIEw = getChildRecyclerVIEw(contentVIEw); if (childRecyclerVIEw != null) {
        //RecyclerVIEw只接收速度参数 childRecyclerVIEw.fling(0, velY); } }
  //通过速度计算距离 @OverrIDe public voID fling(int veLocityY) { super.fling(veLocityY); if (veLocityY <= 0) { this.veLocityY = 0; } else { isstartFling = true; this.veLocityY = veLocityY; } } @OverrIDe protected voID onFinishInflate() { super.onFinishInflate(); topVIEw = ((VIEwGroup) getChildAt(0)).getChildAt(0); contentVIEw = (VIEwGroup) ((VIEwGroup) getChildAt(0)).getChildAt(1); } @OverrIDe protected voID onMeasure(int wIDthMeasureSpec, int heightmeasureSpec) { // 调整contentVIEw的高度为父容器高度,使之填充布局,避免父容器滚动后出现空白 super.onMeasure(wIDthMeasureSpec, heightmeasureSpec); VIEwGroup.LayoutParams lp = contentVIEw.getLayoutParams(); lp.height = getMeasuredHeight(); contentVIEw.setLayoutParams(lp); } @OverrIDe public voID onnestedPreScroll(@NonNull VIEw target, int dx, int dy, @NonNull int[] consumed, int type) { Log.i("nestedScrollLayout", getScrollY()+"::onnestedPreScroll::"+topVIEw.getMeasuredHeight()); // 向上滑动。若当前topvIEw可见,需要将topvIEw滑动至不可见 boolean hIDetop = dy > 0 && getScrollY() < topVIEw.getMeasuredHeight(); if (hIDetop) { scrollBy(0, dy); consumed[1] = dy; } } private RecyclerVIEw getChildRecyclerVIEw(VIEwGroup vIEwGroup) { for (int i = 0; i < vIEwGroup.getChildCount(); i++) { VIEw vIEw = vIEwGroup.getChildAt(i); if (vIEw instanceof RecyclerVIEw && vIEw.getClass() == nestedLogRecyclerVIEw.class) { return (RecyclerVIEw) vIEwGroup.getChildAt(i); } else if (vIEwGroup.getChildAt(i) instanceof VIEwGroup) { VIEwGroup childRecyclerVIEw = getChildRecyclerVIEw((VIEwGroup) vIEwGroup.getChildAt(i)); if (childRecyclerVIEw instanceof RecyclerVIEw) { return (RecyclerVIEw) childRecyclerVIEw; } } continue; } return null; }}

 

计算距离的工具类,Google自己写的,我直接抄的源码,我专业不是物理,所以这玩意直接抄的。。。。。。

public class FlingHelper {    private static float DECELERATION_RATE = ((float) (Math.log(0.78d) / Math.log(0.9d)));    private static float mFlingFriction = VIEwConfiguration.getScrollFriction();    private static float mPhysicalCoeff;    public FlingHelper(Context context) {        mPhysicalCoeff = context.getResources().getdisplayMetrics().density * 160.0f * 386.0878f * 0.84f;    }    private double getSplineDeceleration(int i) {        return Math.log((double) ((0.35f * ((float) Math.abs(i))) / (mFlingFriction * mPhysicalCoeff)));    }    private double getSplineDecelerationBydistance(double d) {        return ((((double) DECELERATION_RATE) - 1.0d) * Math.log(d / ((double) (mFlingFriction * mPhysicalCoeff)))) / ((double) DECELERATION_RATE);    }    public double getSplineFlingdistance(int i) {        return Math.exp(getSplineDeceleration(i) * (((double) DECELERATION_RATE) / (((double) DECELERATION_RATE) - 1.0d))) * ((double) (mFlingFriction * mPhysicalCoeff));    }    public int getVeLocityBydistance(double d) {        return Math.abs((int) (((Math.exp(getSplineDecelerationBydistance(d)) * ((double) mFlingFriction)) * ((double) mPhysicalCoeff)) / 0.3499999940395355d));    }}

至此,功能完成!

总结

以上是内存溢出为你收集整理的Android开发——自定义view之京东淘宝首页二级联动全部内容,希望文章能够帮你解决Android开发——自定义view之京东淘宝首页二级联动所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1056115.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-25
下一篇2022-05-25

发表评论

登录后才能评论

评论列表(0条)

    保存