gpt4 book ai didi

android - 如何在android中同步滚动两个Recyclerviews?

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:14:30 24 4
gpt4 key购买 nike

我正在尝试使用 Recyclerviews 在 android 中创建 EPG。它需要固定顶行水平滚动显示对应时间的节目,固定最左列垂直滚动显示各种 channel 。

基于 this所以回答,我带着下面的design来了

    <?xml version="1.0" encoding="utf-8"?>
<!--Outer container layout-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".MainActivity">

<!--To display Channels list-->
<LinearLayout
android:layout_width="150dp"
android:layout_height="match_parent"
android:orientation="vertical">

<!--Position (0,0)-->
<TextView
android:id="@+id/tv_change_date"
android:layout_width="match_parent"
android:layout_height="50dp" />

<android.support.v7.widget.RecyclerView
android:id="@+id/rcv_channel_name"
android:scrollbars="none"
android:layout_width="150dp"
android:layout_height="match_parent"/>

</LinearLayout>


<!--To display Time and Programs list-->
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">

<!--Time horizontal list-->
<android.support.v7.widget.RecyclerView
android:id="@+id/rcv_vertical_header"
android:scrollbars="none"
android:layout_width="match_parent"
android:layout_height="50dp"/>

<!--Vertical list whose each element is horizontal list to show programs-->
<android.support.v7.widget.RecyclerView
android:id="@+id/rcv_vertical"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

</HorizontalScrollView>
</LinearLayout>

现在,我需要同步 rcv_vertical 和 rcv_channel_name 的垂直滚动。我在这个 github project 中实现了它.

public class SelfRemovingOnScrollListener extends RecyclerView.OnScrollListener {

@Override
public final void onScrollStateChanged(@NonNull final RecyclerView recyclerView, final int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
recyclerView.removeOnScrollListener(this);
}
}
}

在主 Activity 中

private final RecyclerView.OnScrollListener channelScrollListener = new SelfRemovingOnScrollListener() {
@Override
public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {
super.onScrolled(recyclerView, dx, dy);
programsRecyclerView.scrollBy(dx, dy);
}
};
private final RecyclerView.OnScrollListener programScrollListener = new SelfRemovingOnScrollListener() {

@Override
public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {
super.onScrolled(recyclerView, dx, dy);
channelsRecyclerView.scrollBy(dx, dy);
}
};

@Override
protected void onStart() {
super.onStart();
//Sync channel name RCV and Programs RCV scrolling
channelsRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
private int mLastY;

@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
Log.d("debug", "LEFT: onInterceptTouchEvent");

final Boolean ret = rv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE;
if (!ret) {
onTouchEvent(rv, e);
}
return Boolean.FALSE;
}

@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
Log.d("debug", "LEFT: onTouchEvent");


final int action;
if ((action = e.getAction()) == MotionEvent.ACTION_DOWN && programsRecyclerView
.getScrollState() == RecyclerView.SCROLL_STATE_IDLE) {
mLastY = rv.getScrollY();
Log.d("scroll","channelsRecyclerView Y: "+mLastY);
rv.addOnScrollListener(channelScrollListener);
}
else {
if (action == MotionEvent.ACTION_UP && rv.getScrollY() == mLastY) {
rv.removeOnScrollListener(channelScrollListener);
}
}
}

@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
Log.d("debug", "LEFT: onRequestDisallowInterceptTouchEvent");
}
});

programsRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {

private int mLastY;

@Override
public boolean onInterceptTouchEvent(@NonNull final RecyclerView rv, @NonNull final
MotionEvent e) {
Log.d("debug", "RIGHT: onInterceptTouchEvent");

final Boolean ret = rv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE;
if (!ret) {
onTouchEvent(rv, e);
}
return Boolean.FALSE;
}

@Override
public void onTouchEvent(@NonNull final RecyclerView rv, @NonNull final MotionEvent e) {
Log.d("debug", "RIGHT: onTouchEvent");

final int action;
if ((action = e.getAction()) == MotionEvent.ACTION_DOWN && channelsRecyclerView
.getScrollState
() == RecyclerView.SCROLL_STATE_IDLE) {
mLastY = rv.getScrollY();
rv.addOnScrollListener(programScrollListener);
Log.d("scroll","programsRecyclerView Y: "+mLastY);
}
else {
if (action == MotionEvent.ACTION_UP && rv.getScrollY() == mLastY) {
rv.removeOnScrollListener(programScrollListener);
}
}
}

@Override
public void onRequestDisallowInterceptTouchEvent(final boolean disallowIntercept) {
Log.d("debug", "RIGHT: onRequestDisallowInterceptTouchEvent");
}
});
[![}][3]][3]

它工作正常,直到我在 Horizo​​ntalScrollView 中进行水平滚动。之后,左侧的 recyclerview“rcv_channel_name”比右侧的 rcv_vertical 滚动得更快。

非常感谢任何解决此问题的帮助或建议。

最佳答案

已编辑!我终于设法让它工作了!这是我的简短解决方案(另请参阅使用方向键进行基于焦点的输入的旧答案):

首先你需要 3 个回收器(在 Fragment 或 Activity 中):

public RecyclerView vertical_recycler, current_focus_recycler, time_recycler;

然后添加

public int scroll_offset;

此变量将以像素为单位保存滚动偏移以供进一步使用。接下来正确填充您的垂直 RecyclerView,确保您的 RecyclerView 具有水平 RecyclerView 作为子级。还要确保您的 time_recycler 将比任何 horizo​​ntal_recycler 都长(在其适配器的 getItemCount 中返回 Integer.MAX_VALUE 将确保您很好......有点......):

@Override
public int getItemCount() {
return Integer.MAX_VALUE;
}

接下来在Activity或者Fragment中添加滚动监听

time_recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
horizontal_scroll(dx);
if (dx == 0) {
scroll_offset = 0;
} else {
scroll_offset += dx;
}
}
});

public void horizontal_scroll(int dx) {
for (int i = 0, n = vertical_recycler.getChildCount(); i < n; ++i) {
if (vertical_recycler.getChildAt(i).findViewById(R.id.horizontal_recycler) != current_focus_recycler) {
vertical_recycler.getChildAt(i).findViewById(R.id.horizontal_recycler).scrollBy(dx, 0);
}
}
}

在垂直 RecyclerView 的适配器中添加所需的其他功能:

private AdapterSheduleHorizontal adapter_horizontal;

@Override
public void onViewAttachedToWindow(AdapterSheduleVertical.VerticalViewHolder holder){
super.onViewAttachedToWindow(holder);

((LinearLayoutManager) holder.horizontal_recycler.getLayoutManager())
.scrollToPositionWithOffset(0,
-fragmentShedule.scroll_offset);
holder.horizontal_recycler.addOnScrollListener(onScrollListener);

}

@Override
public void onViewDetachedFromWindow(VerticalViewHolder holder) {
super.onViewDetachedFromWindow(holder);
holder.horizontal_recycler.removeOnScrollListener(onScrollListener);
}


RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if(recyclerView == fragmentShedule.current_focus_recycler) {
fragmentShedule.time_recycler.scrollBy(dx, 0);
}

}

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int state) {
super.onScrollStateChanged(recyclerView, state);
switch (state) {
case RecyclerView.SCROLL_STATE_IDLE:
fragmentShedule.current_focus_recycler = null;
break;
}
}


};

@Override
public void onBindViewHolder(VerticalViewHolder sheduleViewHolder, final int position) {

...
LinearLayoutManager lm = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
sheduleViewHolder.horizontal_recycler.setLayoutManager(lm);
adapter_horizontal = new AdapterSheduleHorizontal(fragmentShedule, context, dayItemList);
sheduleViewHolder.horizontal_recycler.setAdapter(adapter_horizontal);
...
}

下一步在水平 RecyclerView 的适配器中

...
RecyclerView parent_recyclerview;
@Override
public void onBindViewHolder(final HorizontalViewHolder sheduleViewHolder, final int i) {


LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);

lp.width = 500;//some width to

sheduleViewHolder.shedule_item.setLayoutParams(lp);

sheduleViewHolder.itemView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {

switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
fragmentShedule.current_focus_recycler = parent_recyclerview;
break;
}

return false;
}
});

sheduleViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "click"+i, Toast.LENGTH_SHORT).show();
}
});


}

@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
parent_recyclerview = recyclerView;
}

我相信这就是全部。请随时发表评论并告诉我它是否不适合您,我会仔细检查我的代码(我已经针对所有子项的垂直和水平滚动测试了它)。


旧答案

好的,我将展示适用于我的类似 EPG 的应用程序的方法。

让我们假设你有 RecyclerView 和 RecyclerView child 。 parent 垂直滚动, child - 水平滚动。在此布局之上,您还有另一个带时间线的 RecyclerView(也可以水平滚动)。

您需要滚动时间轴以滚动所有回收者- child ,除了那个获得焦点/被触摸/正在滚动的 child (如果您不打算使用触摸屏功能,则只能使用以下 child 的焦点水平 View )。

话虽如此,您需要注册当前正在滚动的子 RecyclerView。

public RecyclerView vertical_recycler, current_focus_recycler, time_recycler;

我还注册了一次滚动所有水平 RecyclerView 的方法:

public void horizontal_scroll(int dx) {
for (int i = 0, n = vertical_recycler.getChildCount(); i < n; ++i) {
if (vertical_recycler.getChildAt(i).findViewById(R.id.horizontal_recycler) != current_focus_recycler) {
vertical_recycler.getChildAt(i).findViewById(R.id.horizontal_recycler).scrollBy(dx, 0);
}
}
}

为我的 time_recycler 设置适配器后,我添加了监听器:

time_recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
horizontal_scroll(dx);
}
});

到目前为止一切顺利。比在对应于水平 RecyclerView-child 的适配器中,我添加了:

RecyclerView parent_recyclerview;

RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {


super.onScrolled(recyclerView, dx, dy);
((ActivityMain) context).time_recycler.scrollBy(dx, 0);
}
};

//...

@Override
public void onBindViewHolder(final HorizontalViewHolder viewHolder, final int i) {

viewHolder.shedule_item.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (v.hasFocus()) {
((ActivityMain) context).current_focus_recycler = parent_recyclerview;
parent_recyclerview.addOnScrollListener(onScrollListener);
} else {
parent_recyclerview.removeOnScrollListener(onScrollListener);
}
}
});

}

@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
parent_recyclerview = recyclerView;
}

//...

class HorizontalViewHolder extends RecyclerView.ViewHolder {
private LinearLayout shedule_item;
public HorizontalViewHolder(View view) {
super(view);
shedule_item = (LinearLayout) view.findViewById(R.id.shedule_item);
}
}

水平 RecyclerView 项目的布局是:

<LinearLayout
android:id="@+id/shedule_item"
.../>
...
</LinearLayout>

如果我使用键盘并使用箭头键在 epg 表中导航,这对我来说是有效的。通过一些调整(在 Ember 中有一些痛苦),您将能够拦截从父水平回收器到它的 child 的触摸事件。希望对您有所帮助。

关于android - 如何在android中同步滚动两个Recyclerviews?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39013398/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com