Я использую библиотеку TableView для создания таблицы для данных SQL путем фильтрации, щелчка, прокрутки, сортировки и т. Д.
https://github.com/evrencoskun/TableView
Эта библиотека очень полезна для меня, но есть ошибка в прокрутке RTL, и я хочу использовать таблицу RTL.Когда это только горизонтальная прокрутка, это хорошая работа.Когда он движется только вертикально, хорошая работа сделана.Но когда происходит горизонтальная прокрутка, а затем перемещение вместе по вертикали, стол ломается.Я думаю, причина (вероятно) в том, что TableView не сохраняет положение прокрутки в соответствии с жизненным циклом действия.Думаю, проблема в классе HorizontalRecyclerViewListener и классе VerticalRecyclerViewListener.но я не способен это исправить.
есть ли кто-нибудь, кто может исправить эту ошибку?
скриншот ошибки:
http://atighsoft.ir/download/a.jpg
http://atighsoft.ir/download/b.jpg
Класс HorizontalRecyclerViewListener:
package com.evrencoskun.tableview.listener.scroll;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.MotionEvent;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.adapter.recyclerview.CellRecyclerView;
/**
* Created by evrencoskun on 19/06/2017.
*/
public class HorizontalRecyclerViewListener extends RecyclerView.OnScrollListener implements
RecyclerView.OnItemTouchListener {
private static final String LOG_TAG = HorizontalRecyclerViewListener.class.getSimpleName();
private CellRecyclerView mColumnHeaderRecyclerView;
private RecyclerView.LayoutManager mCellLayoutManager;
private RecyclerView mLastTouchedRecyclerView;
// X position means column position
private int mXPosition;
private boolean mIsMoved;
private int mScrollPosition;
private int mScrollPositionOffset = 0;
private RecyclerView mCurrentRVTouched = null;
private VerticalRecyclerViewListener mVerticalRecyclerViewListener;
public HorizontalRecyclerViewListener(ITableView tableView) {
this.mColumnHeaderRecyclerView = tableView.getColumnHeaderRecyclerView();
this.mCellLayoutManager = tableView.getCellRecyclerView().getLayoutManager();
this.mVerticalRecyclerViewListener = tableView.getVerticalRecyclerViewListener();
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
// Prevent multitouch, once we start to listen with a RV,
// we ignore any other RV until the touch is released (UP)
if (mCurrentRVTouched != null && rv != mCurrentRVTouched) {
return true;
}
if (e.getAction() == MotionEvent.ACTION_DOWN) {
mCurrentRVTouched = rv;
if (rv.getScrollState() == RecyclerView.SCROLL_STATE_IDLE) {
if (mLastTouchedRecyclerView != null && rv != mLastTouchedRecyclerView) {
if (mLastTouchedRecyclerView == mColumnHeaderRecyclerView) {
mColumnHeaderRecyclerView.removeOnScrollListener(this);
mColumnHeaderRecyclerView.stopScroll();
Log.d(LOG_TAG, "Scroll listener has been removed to " +
"mColumnHeaderRecyclerView at last touch control");
} else {
int lastTouchedIndex = getIndex(mLastTouchedRecyclerView);
// Control whether the last touched recyclerView is still attached or not
if (lastTouchedIndex >= 0 && lastTouchedIndex < mCellLayoutManager
.getChildCount()) {
// Control the scroll listener is already removed. For instance; if user
// scroll the parent recyclerView vertically, at that time,
// ACTION_CANCEL
// will be triggered that removes the scroll listener of the last
// touched
// recyclerView.
if (!((CellRecyclerView) mLastTouchedRecyclerView)
.isHorizontalScrollListenerRemoved()) {
// Remove scroll listener of the last touched recyclerView
// Because user touched another recyclerView before the last one get
// SCROLL_STATE_IDLE state that removes the scroll listener
((RecyclerView) mCellLayoutManager.getChildAt(lastTouchedIndex))
.removeOnScrollListener(this);
Log.d(LOG_TAG, "Scroll listener has been removed to " +
mLastTouchedRecyclerView.getId() + " CellRecyclerView " +
"at last touch control");
// the last one scroll must be stopped to be sync with others
((RecyclerView) mCellLayoutManager.getChildAt(lastTouchedIndex))
.stopScroll();
}
}
}
}
mXPosition = ((CellRecyclerView) rv).getScrolledX();
rv.addOnScrollListener(this);
Log.d(LOG_TAG, "Scroll listener has been added to " + rv.getId() + " at action "
+ "down");
}
} else if (e.getAction() == MotionEvent.ACTION_MOVE) {
mCurrentRVTouched = rv;
// Why does it matter ?
// user scroll any recyclerView like brushing, at that time, ACTION_UP will be
// triggered
// before scrolling. So, we need to store whether it moved or not.
mIsMoved = true;
} else if (e.getAction() == MotionEvent.ACTION_UP) {
mCurrentRVTouched = null;
int nScrollX = ((CellRecyclerView) rv).getScrolledX();
// Is it just touched without scrolling then remove the listener
if (mXPosition == nScrollX && !mIsMoved) {
rv.removeOnScrollListener(this);
Log.d(LOG_TAG, "Scroll listener has been removed to " + rv.getId() + " at " +
"action" + " up");
}
mLastTouchedRecyclerView = rv;
} else if (e.getAction() == MotionEvent.ACTION_CANCEL) {
// ACTION_CANCEL action will be triggered if users try to scroll vertically
// For this situation, it doesn't matter whether the x position is changed or not
// Beside this, even if moved action will be triggered, scroll listener won't
// triggered on cancel action. So, we need to change state of the mIsMoved value as
// well.
// Renew the scroll position and its offset
renewScrollPosition(rv);
rv.removeOnScrollListener(this);
Log.d(LOG_TAG, "Scroll listener has been removed to " + rv.getId() + " at action " +
"cancel");
mIsMoved = false;
mLastTouchedRecyclerView = rv;
mCurrentRVTouched = null;
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
// Column Header should be scrolled firstly. Because it is the compared recyclerView to
// make column width fit.
if (recyclerView == mColumnHeaderRecyclerView) {
super.onScrolled(recyclerView, dx, dy);
// Scroll each cell recyclerViews
for (int i = 0; i < mCellLayoutManager.getChildCount(); i++) {
CellRecyclerView child = (CellRecyclerView) mCellLayoutManager.getChildAt(i);
// Scroll horizontally
child.scrollBy(dx, dy);
}
} else {
// Scroll column header recycler view as well
//mColumnHeaderRecyclerView.scrollBy(dx, 0);
super.onScrolled(recyclerView, dx, dy);
// Scroll each cell recyclerViews except the current touched one
for (int i = 0; i < mCellLayoutManager.getChildCount(); i++) {
CellRecyclerView child = (CellRecyclerView) mCellLayoutManager.getChildAt(i);
if (child != recyclerView) {
// Scroll horizontally
child.scrollBy(dx, 0);
}
}
}
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
// Renew the scroll position and its offset
renewScrollPosition(recyclerView);
recyclerView.removeOnScrollListener(this);
Log.d(LOG_TAG, "Scroll listener has been removed to " + recyclerView.getId() + " at "
+ "onScrollStateChanged");
mIsMoved = false;
// When a user scrolls horizontally, VerticalRecyclerView add vertical scroll
// listener because of touching process.However, mVerticalRecyclerViewListener
// doesn't know anything about it. So, it is necessary to remove the last touched
// recyclerView which uses the mVerticalRecyclerViewListener.
boolean isNeeded = mLastTouchedRecyclerView != mColumnHeaderRecyclerView;
mVerticalRecyclerViewListener.removeLastTouchedRecyclerViewScrollListener(isNeeded);
}
}
private int getIndex(RecyclerView rv) {
for (int i = 0; i < mCellLayoutManager.getChildCount(); i++) {
if (mCellLayoutManager.getChildAt(i) == rv) {
return i;
}
}
return -1;
}
/**
* This method calculates the current scroll position and its offset to help new attached
* recyclerView on window at that position and offset
*
* @see #getScrollPosition()
* @see #getScrollPositionOffset()
*/
private void renewScrollPosition(RecyclerView recyclerView) {
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
mScrollPosition = layoutManager.findFirstCompletelyVisibleItemPosition();
// That means there is no completely visible Position.
if (mScrollPosition == -1) {
mScrollPosition = layoutManager.findFirstVisibleItemPosition();
// That means there is just a visible item on the screen
if (mScrollPosition == layoutManager.findLastVisibleItemPosition()) {
// in this case we use the position which is the last & first visible item.
} else {
// That means there are 2 visible item on the screen. However, second one is not
// completely visible.
mScrollPosition = mScrollPosition + 1;
}
}
mScrollPositionOffset = layoutManager.findViewByPosition(mScrollPosition).getLeft();
}
/**
* When parent RecyclerView scrolls vertically, the child horizontal recycler views should be
* displayed on right scroll position. So the first complete visible position of the
* recyclerView is stored as a member to use it for a new attached recyclerview whose
* orientation is horizontal as well.
*
* @see #getScrollPositionOffset()
*/
public int getScrollPosition() {
return mScrollPosition;
}
/**
* Users can scroll the recyclerViews to the any x position which may not the exact position. So
* we need to know store the offset value to locate a specific location for a new attached
* recyclerView
*
* @see #getScrollPosition()
*/
public int getScrollPositionOffset() {
return mScrollPositionOffset;
}
public void setScrollPositionOffset(int offset) {
mScrollPositionOffset = offset;
}
/**
* To change default scroll position that is before TableView is not populated.
*/
public void setScrollPosition(int position) {
this.mScrollPosition = position;
}
}
Класс VerticalRecyclerViewListener:
package com.evrencoskun.tableview.listener.scroll;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.MotionEvent;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.adapter.recyclerview.CellRecyclerView;
/**
* Created by evrencoskun on 30/06/2017.
*/
public class VerticalRecyclerViewListener extends RecyclerView.OnScrollListener implements
RecyclerView.OnItemTouchListener {
private static final String LOG_TAG = VerticalRecyclerViewListener.class.getSimpleName();
private CellRecyclerView mRowHeaderRecyclerView, mCellRecyclerView;
private RecyclerView mLastTouchedRecyclerView;
// Y Position means row position
private int mYPosition;
private boolean mIsMoved;
private RecyclerView mCurrentRVTouched = null;
public VerticalRecyclerViewListener(ITableView tableView) {
this.mRowHeaderRecyclerView = tableView.getRowHeaderRecyclerView();
this.mCellRecyclerView = tableView.getCellRecyclerView();
}
private float dx = 0, dy = 0;
/**
* + * check which direction the user is scrolling
* + * @param ev
* + * @return
* +
*/
private boolean verticalDirection(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_MOVE) {
if (dx == 0) {
dx = ev.getX();
}
if (dy == 0) {
dy = ev.getY();
}
float xdiff = Math.abs(dx - ev.getX());
float ydiff = Math.abs(dy - ev.getY());
dx = ev.getX();
dy = ev.getY();
// if user scrolled more horizontally than vertically
if (xdiff > ydiff) {
return false;
}
}
return true;
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
// If scroll direction is not Vertical, then ignore and reset last RV touched
if (!verticalDirection(e)) {
mCurrentRVTouched = null;
return false;
}
// Prevent multitouch, once we start to listen with a RV,
// we ignore any other RV until the touch is released (UP)
if ((mCurrentRVTouched != null && rv != mCurrentRVTouched)) {
return true;
}
if (e.getAction() == MotionEvent.ACTION_DOWN) {
mCurrentRVTouched = rv;
if (rv.getScrollState() == RecyclerView.SCROLL_STATE_IDLE) {
if (mLastTouchedRecyclerView != null && rv != mLastTouchedRecyclerView) {
removeLastTouchedRecyclerViewScrollListener(false);
}
mYPosition = ((CellRecyclerView) rv).getScrolledY();
rv.addOnScrollListener(this);
if (rv == mCellRecyclerView) {
Log.d(LOG_TAG, "mCellRecyclerView scroll listener added");
} else if (rv == mRowHeaderRecyclerView) {
Log.d(LOG_TAG, "mRowHeaderRecyclerView scroll listener added");
}
// Refresh the value;
mIsMoved = false;
}
} else if (e.getAction() == MotionEvent.ACTION_MOVE) {
mCurrentRVTouched = rv;
// Why does it matter ?
// user scroll any recyclerView like brushing, at that time, ACTION_UP will be
// triggered
// before scrolling. So, we need to store whether it moved or not.
mIsMoved = true;
} else if (e.getAction() == MotionEvent.ACTION_UP) {
mCurrentRVTouched = null;
int nScrollY = ((CellRecyclerView) rv).getScrolledY();
// TODO: Even if moved value is true and it may not scroll. This should be fixed.
// TODO: The scenario is scroll lightly center RecyclerView vertically.
// TODO: Below if condition may be changed later.
// Is it just touched without scrolling then remove the listener
if (mYPosition == nScrollY && !mIsMoved && rv.getScrollState() == RecyclerView
.SCROLL_STATE_IDLE) {
rv.removeOnScrollListener(this);
if (rv == mCellRecyclerView) {
Log.d(LOG_TAG, "mCellRecyclerView scroll listener removed from up ");
} else if (rv == mRowHeaderRecyclerView) {
Log.d(LOG_TAG, "mRowHeaderRecyclerView scroll listener removed from up");
}
}
mLastTouchedRecyclerView = rv;
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
// CellRecyclerViews should be scrolled after the RowHeaderRecyclerView.
// Because it is one of the main compared criterion to make each columns fit.
if (recyclerView == mCellRecyclerView) {
super.onScrolled(recyclerView, dx, dy);
// The below code has been moved in CellLayoutManager
// mRowHeaderRecyclerView.scrollBy(0, dy);
} else if (recyclerView == mRowHeaderRecyclerView) {
super.onScrolled(recyclerView, dx, dy);
mCellRecyclerView.scrollBy(0, dy);
}
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
recyclerView.removeOnScrollListener(this);
mIsMoved = false;
mCurrentRVTouched = null;
if (recyclerView == mCellRecyclerView) {
Log.d(LOG_TAG, "mCellRecyclerView scroll listener removed from " +
"onScrollStateChanged");
} else if (recyclerView == mRowHeaderRecyclerView) {
Log.d(LOG_TAG, "mRowHeaderRecyclerView scroll listener removed from " +
"onScrollStateChanged");
}
}
}
/**
* If current recyclerView that is touched to scroll is not same as the last one, this method
* helps to remove the scroll listener of the last touched recyclerView.
* This method is a little bit different from HorizontalRecyclerViewListener.
*
* @param isNeeded Is mCellRecyclerView scroll listener should be removed ? The scenario is a
* user scrolls vertically using RowHeaderRecyclerView. After that, the user
* scrolls horizontally using ColumnHeaderRecyclerView.
*/
public void removeLastTouchedRecyclerViewScrollListener(boolean isNeeded) {
if (mLastTouchedRecyclerView == mCellRecyclerView) {
mCellRecyclerView.removeOnScrollListener(this);
mCellRecyclerView.stopScroll();
Log.d(LOG_TAG, "mCellRecyclerView scroll listener removed from last touched");
} else {
mRowHeaderRecyclerView.removeOnScrollListener(this);
mRowHeaderRecyclerView.stopScroll();
Log.d(LOG_TAG, "mRowHeaderRecyclerView scroll listener removed from last touched");
if (isNeeded) {
mCellRecyclerView.removeOnScrollListener(this);
mCellRecyclerView.stopScroll();
Log.d(LOG_TAG, "mCellRecyclerView scroll listener removed from last touched");
}
}
}
}
Класс ScrollHandler:
/*
* Copyright (c) 2018. Evren Coşkun
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.evrencoskun.tableview.handler;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.layoutmanager.CellLayoutManager;
import com.evrencoskun.tableview.layoutmanager.ColumnHeaderLayoutManager;
import com.evrencoskun.tableview.layoutmanager.ColumnLayoutManager;
/**
* Created by evrencoskun on 13.01.2018.
*/
public class ScrollHandler {
private ITableView mTableView;
private CellLayoutManager mCellLayoutManager;
private LinearLayoutManager mRowHeaderLayoutManager;
private ColumnHeaderLayoutManager mColumnHeaderLayoutManager;
public ScrollHandler(ITableView tableView) {
this.mTableView = tableView;
this.mCellLayoutManager = tableView.getCellLayoutManager();
this.mRowHeaderLayoutManager = tableView.getRowHeaderLayoutManager();
this.mColumnHeaderLayoutManager = tableView.getColumnHeaderLayoutManager();
}
public void scrollToColumnPosition(int columnPosition) {
// TableView is not on screen yet.
if (!((View) mTableView).isShown()) {
// Change default value of the listener
mTableView.getHorizontalRecyclerViewListener().setScrollPosition(columnPosition);
}
// Column Header should be scrolled firstly because of fitting column width process.
scrollColumnHeader(columnPosition, 0);
scrollCellHorizontally(columnPosition, 0);
}
public void scrollToColumnPosition(int columnPosition, int offset) {
// TableView is not on screen yet.
if (!((View) mTableView).isShown()) {
// Change default value of the listener
mTableView.getHorizontalRecyclerViewListener().setScrollPosition(columnPosition);
mTableView.getHorizontalRecyclerViewListener().setScrollPositionOffset(offset);
}
// Column Header should be scrolled firstly because of fitting column width process.
scrollColumnHeader(columnPosition, offset);
scrollCellHorizontally(columnPosition, offset);
}
public void scrollToRowPosition(int rowPosition) {
mRowHeaderLayoutManager.scrollToPosition(rowPosition);
mCellLayoutManager.scrollToPosition(rowPosition);
}
public void scrollToRowPosition(int rowPosition, int offset) {
mRowHeaderLayoutManager.scrollToPositionWithOffset(rowPosition, offset);
mCellLayoutManager.scrollToPositionWithOffset(rowPosition, offset);
}
private void scrollCellHorizontally(int columnPosition, int offset) {
CellLayoutManager cellLayoutManager = mTableView.getCellLayoutManager();
for (int i = cellLayoutManager.findFirstVisibleItemPosition(); i < cellLayoutManager
.findLastVisibleItemPosition() + 1; i++) {
RecyclerView cellRowRecyclerView = (RecyclerView) cellLayoutManager
.findViewByPosition(i);
if (cellRowRecyclerView != null) {
ColumnLayoutManager columnLayoutManager = (ColumnLayoutManager)
cellRowRecyclerView.getLayoutManager();
columnLayoutManager.scrollToPositionWithOffset(columnPosition, offset);
}
}
}
private void scrollColumnHeader(int columnPosition, int offset) {
mTableView.getColumnHeaderLayoutManager().scrollToPositionWithOffset(columnPosition,
offset);
}
public int getColumnPosition() {
return mColumnHeaderLayoutManager.findFirstVisibleItemPosition();
}
public int getColumnPositionOffset() {
View child = mColumnHeaderLayoutManager.findViewByPosition(mColumnHeaderLayoutManager
.findFirstVisibleItemPosition());
if(child != null) {
return child.getRight();
}
return 0;
}
public int getRowPosition() {
return mRowHeaderLayoutManager.findFirstVisibleItemPosition();
}
public int getRowPositionOffset() {
View child = mRowHeaderLayoutManager.findViewByPosition(mRowHeaderLayoutManager
.findFirstVisibleItemPosition());
if(child != null) {
return child.getRight();
}
return 0;
}
}