GVKun编程网logo

使用 ViewPager 实现日/日历视图:Android(android日历展示)

2

本文将分享使用ViewPager实现日/日历视图:Android的详细内容,并且还将对android日历展示进行详尽解释,此外,我们还将为大家带来关于021Android日历视图(Calendarvi

本文将分享使用 ViewPager 实现日/日历视图:Android的详细内容,并且还将对android日历展示进行详尽解释,此外,我们还将为大家带来关于021 Android 日历视图(Calendarview)、Android Fragment+ViewPager 懒加载、Android NestedScrolling 实现 ViewPager 列表切换、Android NestedScrollView 嵌套 ViewPager 以及 ViewPager 内嵌套 recyclerView 的问题的相关知识,希望对你有所帮助。

本文目录一览:

使用 ViewPager 实现日/日历视图:Android(android日历展示)

使用 ViewPager 实现日/日历视图:Android(android日历展示)

如何解决使用 ViewPager 实现日/日历视图:Android

我有一个 Android 应用,用于跟踪健身房锻炼情况。 我根据当天的时间戳使用 Room 保存锻炼。

我想先显示当前日期,然后才能滑动过去和功能

我使用了一个 viewpager 和 2 种方法:

  • 首先是计算今天 + 和 - 一个月的日期并将列表提供给 viewpager 适配器,这最终会一次加载太多片段

  • 第二个是在适配器中只有 3 个片段的列表(选定日期 +/- 一天),然后在用户滑动时替换片段,这仅适用于通过添加一个项目来滑动到未来到列表的末尾,并删除第一个。

对于我的问题还有其他建议或解决方案吗? 谢谢!

021 Android 日历视图(Calendarview)

021 Android 日历视图(Calendarview)

1.介绍

2.常用属性

3.xml文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button" />

    <CalendarView
        android:id="@+id/calendarView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

4.java后台

对应的工程名:test26

package com.lucky.test26calendar;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CalendarView;
import android.widget.Toast;

import java.text.SimpleDateFormat;


public class MainActivity extends AppCompatActivity {

    CalendarView calendarView;
    Button button;
    String timeStr="";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        calendarView=findViewById(R.id.calendarView);
        button=findViewById(R.id.button);
        //设置时间显示格式
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  //yyyy表示年,MM表示月,dd表示天,HH表示时,mm表示分,ss表示秒
        timeStr=simpleDateFormat.format(calendarView.getDate()); //将calendarView获取的时间样式转化为simpleDateFormat所设置的样式
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this,timeStr,Toast.LENGTH_SHORT).show();
            }
        });

        calendarView.setOnDateChangeListener(new CalendarView.OnDateChangeListener() {
            @Override
            public void onSelectedDayChange( CalendarView view, int year, int month, int dayOfMonth) {
                timeStr=year+"-"+month+"-"+dayOfMonth;
            }
        });

    }
}

 

Android Fragment+ViewPager 懒加载

Android Fragment+ViewPager 懒加载

public abstract class BaseLazyFragment extends BaseFragment {

    private boolean isFragmentVisible;
    private boolean isFirstVisible = true;
    private View rootView;
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        //如果setUserVisibleHint()在rootView创建前调用时,那么
        //就等到rootView创建完后才回调onFragmentVisibleChange(true)
        //保证onFragmentVisibleChange()的回调发生在rootView创建完成之后,以便支持ui操作
        if (rootView == null) {
            rootView = view;
            if (getUserVisibleHint()) {
                if (isFirstVisible) {
                    onLoadData();
                    isFirstVisible = false;
                }
                onFragmentVisibleChange(true);
                isFragmentVisible = true;
            }
        }
        super.onViewCreated(rootView, savedInstanceState);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        initVariable();
    }

    private void initVariable() {
        isFirstVisible = true;
        isFragmentVisible = false;
        rootView = null;
    }



    /**
     * 去除setUserVisibleHint()多余的回调场景,保证只有当fragment可见状态发生变化时才回调
     * 回调时机在view创建完后,所以支持ui操作,解决在setUserVisibleHint()里进行ui操作有可能报null异常的问题
     * <p>
     * 可在该回调方法里进行一些ui显示与隐藏,比如加载框的显示和隐藏
     *
     * @param isVisible true  不可见 -> 可见
     *                  false 可见  -> 不可见
     */
    protected void onFragmentVisibleChange(boolean isVisible) {

    }

    /**
     * 在fragment首次可见时回调,可在这里进行加载数据,保证只在第一次打开Fragment时才会加载数据,
     * 这样就可以防止每次进入都重复加载数据
     * 该方法会在 onFragmentVisibleChange() 之前调用,所以第一次打开时,可以用一个全局变量表示数据下载状态,
     * 然后在该方法内将状态设置为下载状态,接着去执行下载的任务
     * 最后在 onFragmentVisibleChange() 里根据数据下载状态来控制下载进度ui控件的显示与隐藏
     */
    protected void onLoadData() {
    }

    public boolean isFragmentVisible() {
        return isFragmentVisible;
    }

    /**
     * setUserVisibleHint()在Fragment创建时会先被调用一次,传入isVisibleToUser = false
     * 如果当前Fragment可见,那么setUserVisibleHint()会再次被调用一次,传入isVisibleToUser = true
     * 如果Fragment从可见->不可见,那么setUserVisibleHint()也会被调用,传入isVisibleToUser = false
     * 总结:setUserVisibleHint()除了Fragment的可见状态发生变化时会被回调外,在new Fragment()时也会被回调
     * 如果我们需要在 Fragment 可见与不可见时干点事,用这个的话就会有多余的回调了,那么就需要重新封装一个
     *
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        //setUserVisibleHint()有可能在fragment的生命周期外被调用
        if (rootView == null) {
            return;
        }

        if (isFirstVisible && isVisibleToUser) {
            onLoadData();
            isFirstVisible = false;
        }

        if (isVisibleToUser) {
            onFragmentVisibleChange(true);
            isFragmentVisible = true;
            return;
        }

        if (isFragmentVisible) {
            isFragmentVisible = false;
            onFragmentVisibleChange(false);
        }
    }
}

/**
 * Called immediately after {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}
 * has returned, but before any saved state has been restored in to the view.
 * This gives subclasses a chance to initialize themselves once
 * they know their view hierarchy has been completely created.  The fragment''s
 * view hierarchy is not however attached to its parent at this point.
 * @param view The View returned by {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.
 * @param savedInstanceState If non-null, this fragment is being re-constructed
 * from a previous saved state as given here.
 */
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
}

onViewCreated(View view, @Nullable Bundle savedInstanceState) 在 onCreateView (LayoutInflater, ViewGroup, Bundle) 之后调用

Android NestedScrolling 实现 ViewPager 列表切换

Android NestedScrolling 实现 ViewPager 列表切换

一、需求简介

app 首页中经常要实现首页头卡共享,tab 吸顶,内容区通过 ViewPager 切换的需求,以前往往是利用事件处理来完成,还有 Google 官方也提供了相关的库,但是这些也有一定的弊端,适配起来还是比较复杂。这里我们利用 NestedScrolling 机制来实现,之前的博客中也有一篇类似的博客《Android 利用 NestedScrolling 联动机制为 RecyclerView 添加 Header》。这个版本实际上在之前的版本上进行了一些扩展,使其能支持 ViewPager。

当然也有很多开源项目,发现存在的问题很多面,主要问题如下:

  • 头部和内容区域不联动
  • 没有中断 RecyclerView 的 fling 效果,导致 RecyclerView 抢占 ViewPager 事件
  • 侵入式设计太多(当然,本篇方案解决 RecyclerView 中断 fling 时用了侵入式设计)

二、效果展示

三、代码实现

要点:

头部不联动问题:

我们需要处理在 dispatchTouchEvent 或者利用 onInteceptTouchEvent + onTouchEvent 处理,主要处理 VelocityTracker + fling 事件。

RecyclerView 中断 fling 效果问题:

RecyclerView 没有在 stopNestedScroll () 方法中中断滑动,因此需要通过侵入方式,调用 stopScroll () 去完成

主要代码

public class NestedPagerRecyclerViewLayout extends FrameLayout implements NestedScrollingParent2 {
    private final int mFlingVelocity;
    private int mHeadExpandedOffset;
    private float startEventX = 0;
    private float startEventY = 0;
    private float mSlopTouchScale = 0;
    private boolean isTouchMoving = false;
    private View mHeaderView = null;
    private View mBodyView = null;
    private View mVerticalScrollView = null;
    private VelocityTracker mVelocityTracker;
    private NestedScrollingParentHelper parentHelper = new NestedScrollingParentHelper(this);

    public NestedPagerRecyclerViewLayout(@NonNull Context context) {
        this(context, null);
    }

    public NestedPagerRecyclerViewLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public NestedPagerRecyclerViewLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        if (attrs != null) {
            final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NestedPagerRecyclerViewLayout);
            mHeadExpandedOffset = a.getDimensionPixelSize(R.styleable.NestedPagerRecyclerViewLayout_headExpandedOffset, 0);
            a.recycle();
        }

        mSlopTouchScale = ViewConfiguration.get(context).getScaledTouchSlop();
        mFlingVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();
        setClickable(true);
    }

    /**
     * 头部余留偏移
     *
     * @param headExpandedOffset
     */
    public void setHeadExpandOffset(int headExpandedOffset) {
        this.mHeadExpandedOffset = headExpandedOffset;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int childCount = getChildCount();
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int overScrollExtent = overScrollExtent();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            if (lp.childLayoutType == LayoutParams.TYPE_BODY) {
                final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                        getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin
                                + 0, lp.width);
                final int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                        getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin
                                + 0, height - overScrollExtent);
                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        }
    }

    public boolean canScrollVertically(int direction) {
        final int offset = computeVerticalScrollOffset();
        final int range = computeVerticalScrollRange() - computeVerticalScrollExtent();
        if (range == 0) return false;
        if (direction < 0) {
            return offset > 0;
        } else {
            return offset < range;
        }
    }

    @Override
    protected int computeVerticalScrollRange() {
        int childCount = getChildCount();
        if (childCount == 0) return super.computeVerticalScrollRange();
        int range = getPaddingBottom() + getPaddingTop();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            range += child.getHeight() + lp.bottomMargin + lp.topMargin;
        }
        if (range < getHeight()) {
            return super.computeVerticalScrollRange();
        }
        return range;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mHeaderView = getChildView(LayoutParams.TYPE_HEAD);
        mBodyView = getChildView(LayoutParams.TYPE_BODY);
        int childLeft = getPaddingLeft();
        int childTop = getPaddingTop();
        if (mHeaderView != null) {
            LayoutParams lp = (LayoutParams) mHeaderView.getLayoutParams();
            mHeaderView.layout(childLeft + lp.leftMargin, childTop + lp.topMargin, childLeft + lp.leftMargin + mHeaderView.getMeasuredWidth(), childTop + lp.topMargin + mHeaderView.getMeasuredHeight());
            childTop += mHeaderView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
        }
        if (mBodyView != null) {
            LayoutParams lp = (LayoutParams) mBodyView.getLayoutParams();
            mBodyView.layout(childLeft + lp.leftMargin, childTop + lp.topMargin, childLeft + lp.leftMargin + mBodyView.getMeasuredWidth(), childTop + lp.topMargin + mBodyView.getMeasuredHeight());
        }
    }

    protected int overScrollExtent() {
        return Math.max(mHeadExpandedOffset, 0);
    }

    private View getHeaderView() {
        return mHeaderView;
    }

    private View getBodyView() {
        return mBodyView;
    }

    private View findTouchView(float currentX, float currentY) {

        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            float childX = (child.getX() - getScrollX());
            float childY = (child.getY() - getScrollY());
            if (currentX < childX || currentX > (childX + child.getWidth())) {
                continue;
            }
            if (currentY < childY || currentY > (childY + child.getHeight())) {
                continue;
            }
            return child;
        }
        return null;
    }

    private boolean hasHeader() {
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
            if (lp.childLayoutType == LayoutParams.TYPE_HEAD) {
                return true;
            }
        }
        return false;
    }

    public View getChildView(int layoutType) {
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
            if (lp.childLayoutType == layoutType) {
                return getChildAt(i);
            }
        }
        return null;
    }

    private boolean hasBody() {
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
            if (lp.childLayoutType == LayoutParams.TYPE_BODY) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void addView(View child) {
        assertLayoutType(child);
        super.addView(child);
    }

    private void assertLayoutType(View child) {
        ViewGroup.LayoutParams lp = child.getLayoutParams();
        assertLayoutParams(lp);
    }

    private void assertLayoutParams(ViewGroup.LayoutParams lp) {

        if (hasHeader() && hasBody()) {
            throw new IllegalStateException("header and body has already existed");
        }
        if (hasHeader()) {
            if (!(lp instanceof LayoutParams)) {
                throw new IllegalStateException("header should keep only one");
            }
            if (((LayoutParams) lp).childLayoutType == LayoutParams.TYPE_HEAD) {
                throw new IllegalStateException("header should keep only one");
            }
        }
        if (hasBody()) {
            if ((lp instanceof LayoutParams) && ((LayoutParams) lp).childLayoutType == LayoutParams.TYPE_BODY) {
                throw new IllegalStateException("header should keep only one");
            }
        }
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        assertLayoutParams(params);
        super.addView(child, index, params);
    }

    @Override
    public void addView(View child, int index) {
        assertLayoutType(child);
        super.addView(child, index);
    }

    @Override
    public void addView(View child, int width, int height) {
        assertLayoutParams(new LinearLayout.LayoutParams(width, height));
        super.addView(child, width, height);
    }

    @Override
    public void onViewAdded(View child) {
        super.onViewAdded(child);
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LayoutParams;
    }

    @Override
    protected FrameLayout.LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    }

    @Override
    public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
        return new LayoutParams(lp);
    }

    @Override
    public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes, int type) {
        if (axes == SCROLL_AXIS_VERTICAL) {
            //只关注垂直方向的移动
            int maxOffset = computeVerticalScrollRange() - computeVerticalScrollExtent();
            int offset = computeVerticalScrollOffset();
            if (offset <= maxOffset) {
                mVerticalScrollView = target;
                return true;
            }
        } else {
            mVerticalScrollView = null;
        }
        return false;
    }

    @Override
    protected int computeVerticalScrollExtent() {
        int computeVerticalScrollExtent = super.computeVerticalScrollExtent();
        return computeVerticalScrollExtent;
    }

    @Override
    public int getNestedScrollAxes() {
        return parentHelper.getNestedScrollAxes();
    }

    @Override
    public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes, int type) {
        parentHelper.onNestedScrollAccepted(child, target, axes, type);
    }

    @Override
    public void onStopNestedScroll(@NonNull View target, int type) {
        if (mVerticalScrollView == target) {
            Log.d("onNestedScroll", "::::onStopNestedScroll vertical");
            parentHelper.onStopNestedScroll(target, type);

        }
    }

    @Override
    public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
        Log.e("onNestedScroll", "::::onNestedScroll 11111");
    }

    @Override
    public void onNestedPreScroll(@NonNull View target, int dx, int dy, @Nullable int[] consumed, int type) {
        int scrollRange = computeVerticalScrollRange();
        if (scrollRange <= getHeight()) {
            return;
        }
        if (target == null) return;
        if (mVerticalScrollView != target) {
            return;
        }
        Log.e("onNestedScroll", "::::onNestedPreScroll 00000");

        handleVerticalNestedScroll(dx, dy, consumed);

    }

    private void handleVerticalNestedScroll(int dx, int dy, @Nullable int[] consumed) {
        if (dy == 0) {
            return;
        }
        if (!canNestedScrollView(mVerticalScrollView)) {
            //这里要判断向上滑动问题,
            // 如果当前布局可以向上滑动,优先滑动,不然头部可能出现露一半但无法向上滑动的问题
            if (dy < 0) {
                return;
            }
            if (!allowScroll(dy)) {
                return;
            }
        }
        int maxOffset = computeVerticalScrollRange() - computeVerticalScrollExtent();
        int scrollOffset = computeVerticalScrollOffset();

        int dyOffset = dy;
        int targetOffset = scrollOffset + dy;
        if (targetOffset >= maxOffset) {
            dyOffset = maxOffset - scrollOffset;
        }
        if (targetOffset <= 0) {
            dyOffset = 0 - scrollOffset;
        }
        if (!canScrollVertically(dyOffset)) {
            return;
        }
        consumed[1] = dyOffset;
        Log.d("onNestedScroll", "::::" + dyOffset + "+" + scrollOffset + "=" + (scrollOffset + dyOffset));
        scrollBy(0, dyOffset);
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int scrollRange = computeVerticalScrollRange();
        if (scrollRange <= getHeight()) {
            return super.dispatchTouchEvent(event);
        }
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mVelocityTracker.addMovement(event);
                startEventX = event.getX();
                startEventY = event.getY();
                isTouchMoving = false;
                if (mVerticalScrollView instanceof RecyclerView) {
                    /**
                     *RecyclerView 虽然继承了NestedScrollingChild,但是没有在stopNestedScroll中停止
                     *调用stopScroll,导致滑动状态事件自动捕获,造成ViewPager切换问题,这里使用stopScroll()侵入式调用
                     */
                    ((RecyclerView) mVerticalScrollView).stopScroll();
                } else if (mVerticalScrollView instanceof NestedScrollingChild) {
                    mVerticalScrollView.stopNestedScroll();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                float currentX = event.getX();
                float currentY = event.getY();
                float dx = currentX - startEventX;
                float dy = currentY - startEventY;
                if (!isTouchMoving && Math.abs(dy) < Math.abs(dx)) {
                    startEventX = currentX;
                    startEventY = currentY;
                    break;
                }
                View touchView = null;
                int offset = (int) -dy;
                if (!isTouchMoving && Math.abs(dy) >= mSlopTouchScale) {
                    touchView = findTouchView(currentX, currentY);
                    //这里只关注头卡触摸事件即可
                    isTouchMoving = touchView != null && touchView == getHeaderView();
                }
                if (isTouchMoving && !allowScroll(offset)) {
                    isTouchMoving = false;
                }
                startEventX = currentX;
                startEventY = currentY;
                if (!isTouchMoving) {
                    break;
                }
                mVelocityTracker.addMovement(event);
                int maxOffset = computeVerticalScrollRange() - computeVerticalScrollExtent();
                int scrollOffset = computeVerticalScrollOffset();
                int targetOffset = scrollOffset + offset;
                if (targetOffset >= maxOffset) {
                    offset = maxOffset - scrollOffset;
                }
                if (targetOffset <= 0) {
                    offset = 0 - scrollOffset;
                }
                if (offset != 0) {
                    scrollBy(0, offset);
                }
                Log.d("onNestedScroll", ">:>:>" + offset + "+" + scrollOffset + "=" + (scrollOffset + offset));
                super.dispatchTouchEvent(event);
                return true;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_OUTSIDE:
                mVelocityTracker.addMovement(event);
                if (isTouchMoving) {
                    isTouchMoving = false;
                    mVelocityTracker.computeCurrentVelocity(1000, mFlingVelocity);
                    startFling(mVelocityTracker, (int) event.getX(), (int) event.getY());
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                break;
        }

        return super.dispatchTouchEvent(event);
    }

    public boolean allowScroll(int dy) {
        int maxOffset = computeVerticalScrollRange() - computeVerticalScrollExtent();
        int scrollOffset = computeVerticalScrollOffset();
        int dyOffset = dy;
        int targetOffset = scrollOffset + dy;
        if (targetOffset >= maxOffset) {
            dyOffset = maxOffset - scrollOffset;
        }
        if (targetOffset <= 0) {
            dyOffset = 0 - scrollOffset;
        }
        if (!canScrollVertically(dyOffset)) {
            return false;
        }
        return true;
    }

    private void startFling(VelocityTracker velocityTracker, int x, int y) {
        int xVolecity = (int) velocityTracker.getXVelocity();
        int yVolecity = (int) velocityTracker.getYVelocity();
        if (mVerticalScrollView instanceof NestedScrollingChild) {
            Log.d("onNestedScroll", "onNestedScrollfling xVolecity=" + xVolecity + ", yVolecity=" + yVolecity);
            ((RecyclerView) mVerticalScrollView).fling(xVolecity, -yVolecity);
        }
    }

    private boolean canNestedScrollView(View view) {
        if (view == null) {
            return false;
        }
        if (view instanceof RecyclerView) {
            //显示区域最上面一条信息的position
            RecyclerView.LayoutManager manager = ((RecyclerView) view).getLayoutManager();
            if (manager == null) {
                return true;
            }
            if (manager.getChildCount() == 0) {
                return true;
            }
            int scrollOffset = ((RecyclerView) view).computeVerticalScrollOffset();
            return scrollOffset <= 0;
        }
        if (view instanceof NestedScrollingChild) {
            return view.canScrollVertically(-1);
        }
        if (!(view instanceof ViewGroup) && (view instanceof View)) {
            return true;
        }
        throw new IllegalArgumentException("不支持非NestedScrollingChild子类ViewGroup");
    }

    public static class LayoutParams extends FrameLayout.LayoutParams {
        public final static int TYPE_HEAD = 0;
        public final static int TYPE_BODY = 1;
        private int childLayoutType = TYPE_HEAD;

        public LayoutParams(@NonNull Context c, @Nullable AttributeSet attrs) {
            super(c, attrs);
            if (attrs == null) return;
            final TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.NestedPagerRecyclerViewLayout);
            childLayoutType = a.getInt(R.styleable.NestedPagerRecyclerViewLayout_layoutScrollNestedType, 0);
            a.recycle();
        }

        public LayoutParams(int width, int height) {
            super(width, height);
        }

        public LayoutParams(@NonNull ViewGroup.LayoutParams source) {
            super(source);
        }

        public LayoutParams(@NonNull MarginLayoutParams source) {
            super(source);
        }
    }
}

属性定义:

  <declare-styleable name="NestedPagerRecyclerViewLayout">
        <attr name="layoutScrollNestedType" format="flags">
            <flag name="Head" value="0"/>
            <flag name="Body" value="1"/>
        </attr>
        <attr name="headExpandedOffset" format="dimension|reference" />
    </declare-styleable>

布局文件

<?xml version="1.0" encoding="utf-8"?>
<com.smartian.widget.NestedPagerRecyclerViewLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/NestedScrollChildLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:focusable="true"
    android:focusableInTouchMode="true"
    app:headExpandedOffset="45dp">

    <LinearLayout
        android:id="@+id/head"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:orientation="vertical"
        app:layoutScrollNestedType="Head">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="0dip"
            android:layout_weight="1"
            android:background="@color/colorAccent"
            android:gravity="center"
            android:text="top Head" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="45dp">

            <TextView
                android:id="@+id/tab1"
                android:layout_width="0dip"
                android:layout_height="45dp"
                android:layout_weight="1"
                android:background="@android:color/white"
                android:gravity="center"
                android:text="我是tab1" />

            <View
                android:layout_width="1dip"
                android:layout_height="match_parent"
                android:background="@color/colorAccent" />

            <TextView
                android:id="@+id/tab2"
                android:layout_width="0dip"
                android:layout_height="45dp"
                android:layout_weight="1"
                android:background="@android:color/white"
                android:gravity="center"
                android:text="我是tab2" />
        </LinearLayout>
    </LinearLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/body"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorPrimary"
        app:layoutScrollNestedType="Body" />

</com.smartian.widget.NestedPagerRecyclerViewLayout>

至此,我们的方案基本实现了,使用方式如下

public class MyNestedScrollViewActivity extends Activity implements View.OnClickListener {
    private ViewPager viewPager;
    private NestedPagerRecyclerViewLayout scrollChildLayout;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_nested_scrolling_child_layout);
        scrollChildLayout = findViewById(R.id.NestedScrollChildLayout);
        scrollChildLayout.setHeadExpandOffset((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,45,getResources().getDisplayMetrics()));
        viewPager = findViewById(R.id.body);

        findViewById(R.id.tab1).setOnClickListener(this);
        findViewById(R.id.tab2).setOnClickListener(this);

        viewPager.setAdapter(new PagerAdapter() {
            @Override
            public int getCount() {
                return 2;
            }

            @Override
            public boolean isViewFromObject(@NonNull  View view, Object object) {
                return view==object;
            }

            @Override
            public void destroyItem(@NonNull  ViewGroup container, int position, @NonNull  Object object) {
                container.addView((View) object);
            }

            @NonNull
            @Override
            public Object instantiateItem(@NonNull ViewGroup container, int position) {
                View layoutView = LayoutInflater.from(container.getContext()).inflate(R.layout.fragment_recycler_view, container, false);
                RecyclerView recyclerView = layoutView.findViewById(R.id.recycler_view);
                recyclerView.setLayoutManager(new LinearLayoutManager(container.getContext()));
                SimpleRecyclerAdapter adapter = new SimpleRecyclerAdapter(container.getContext(), position%2==0?getData():getData2());
                recyclerView.setAdapter(adapter);
                container.addView(layoutView);
                return layoutView;
            }
        });

    }
    private List<String> getData() {
        List<String> data = new ArrayList<>();
        data.add("#ff9999");
        data.add("#ffaa77");
        data.add("#ff9966");
        data.add("#ffcc55");
        data.add("#ff99bb");
        data.add("#ff77dd");
        data.add("#ff33bb");
        data.add("#ff9999");
        data.add("#ffaa77");
        data.add("#ff9966");
        data.add("#ffcc55");
        return data;
    }
    private List<String> getData2() {
        List<String> data = new ArrayList<>();
        data.add("#9999ff");
        data.add("#aa77ff");
        data.add("#9966ff");
        data.add("#cc55ff");
        data.add("#99bbff");
        data.add("#77ddff");
        data.add("#33bbff");
        data.add("#9999ff");
        data.add("#aa77ff");
        data.add("#9966ff");
        data.add("#cc55ff");
        return data;
    }
    @Override
    public void onClick(View v) {
        int id = v.getId();
        if(id==R.id.tab1){
            viewPager.setCurrentItem(0,true);
        }else if(id==R.id.tab2){
            viewPager.setCurrentItem(1,true);
        }
    }
}

 

Android NestedScrollView 嵌套 ViewPager 以及 ViewPager 内嵌套 recyclerView 的问题

Android NestedScrollView 嵌套 ViewPager 以及 ViewPager 内嵌套 recyclerView 的问题

NestedScrollView 嵌套 viewpager 会出现两个问题

1.viewpager 无法显示,高度为 0

2. 嵌套后 NestedScrollView 无法滑动

先说第一个问题:
viewpager 无法正常显示,只需要在 NestedScrollView 内加入
android:fillViewport="true" 就可以了

  <android.support.v4.widget.NestedScrollView
            android:id="@+id/novice_live_scroll"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:overScrollMode="never"
            android:fillViewport="true"
            >

第二个问题,嵌套后 NestedScrollView 无法滑动,这个问题只要我们重写一个 viewpager,然后在 xml 里用我们的 viewpager 就可以了

public class WrapContentHeightViewPager extends ViewPager {
    public WrapContentHeightViewPager(Context context) {
        super(context);  
    }  
  
    public WrapContentHeightViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);  
    }  
  
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  
        int height = 0;  
        for (int i = 0; i < getChildCount(); i++) {  
            View child = getChildAt(i);
            child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));  
            int h = child.getMeasuredHeight();  
            if (h > height) height = h;  
        }  
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);  
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
    }  
}  

如果 viewpager 内还嵌套了 recycleview,只想让 nestedScrollView 滑动,recyclerView 只用来展示,那么还需要对 recyclerview 进行处理,如下:

   LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity(), 		   LinearLayoutManager.VERTICAL, false) {
            @Override
            public boolean canScrollVertically() {
                return false;
            }
        };
    recycler.setLayoutManager(linearLayoutManager);

记录一个发现的小坑,有遇到这些问题的朋友,希望这个帖子可以帮到大家。

关于使用 ViewPager 实现日/日历视图:Androidandroid日历展示的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于021 Android 日历视图(Calendarview)、Android Fragment+ViewPager 懒加载、Android NestedScrolling 实现 ViewPager 列表切换、Android NestedScrollView 嵌套 ViewPager 以及 ViewPager 内嵌套 recyclerView 的问题等相关内容,可以在本站寻找。

本文标签: