本文将分享使用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日历展示)
- 021 Android 日历视图(Calendarview)
- Android Fragment+ViewPager 懒加载
- Android NestedScrolling 实现 ViewPager 列表切换
- Android NestedScrollView 嵌套 ViewPager 以及 ViewPager 内嵌套 recyclerView 的问题
使用 ViewPager 实现日/日历视图:Android(android日历展示)
如何解决使用 ViewPager 实现日/日历视图:Android
我有一个 Android 应用,用于跟踪健身房锻炼情况。 我根据当天的时间戳使用 Room 保存锻炼。
我想先显示当前日期,然后才能滑动过去和功能
我使用了一个 viewpager 和 2 种方法:
首先是计算今天 + 和 - 一个月的日期并将列表提供给 viewpager 适配器,这最终会一次加载太多片段
第二个是在适配器中只有 3 个片段的列表(选定日期 +/- 一天),然后在用户滑动时替换片段,这仅适用于通过添加一个项目来滑动到未来到列表的末尾,并删除第一个。
对于我的问题还有其他建议或解决方案吗? 谢谢!
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 懒加载
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 列表切换
一、需求简介
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 的问题
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 实现日/日历视图:Android和android日历展示的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于021 Android 日历视图(Calendarview)、Android Fragment+ViewPager 懒加载、Android NestedScrolling 实现 ViewPager 列表切换、Android NestedScrollView 嵌套 ViewPager 以及 ViewPager 内嵌套 recyclerView 的问题等相关内容,可以在本站寻找。
本文标签: