私は現在、毎週の時間枠を編集するためのアプリを実現しようとしています。 ソケットのメカニカルタイマーと似ていますが、私の場合は毎日毎日です。選択可能なRecyclerViewパフォーマンスの問題
粒度は(私はそれが15又は30分であろう推測)二次最初の場所にあります。
私のアプローチはRecyclerView
であり、GridLayoutManager
とArrayAdapter
で、すべてのセルに項目がありました。
さらに多くのセルを選択するには、セルを長押しして他のセルにドラッグすることができます。これを達成するために、私は以下の図書館DragSelectRecyclerViewのリスナーを使用しました。
これはかなりうまく動作しますが、エミュレータや古い携帯電話では非常に遅くて遅いですが、非常に良い項目を選択できます。デバッグログカットでは、ビューのレンダリング時に多数のフレームをスキップし、複数のセルを選択する必要があることも確認できます。
そのような行動を達成するためのより良い方法はあります。それとも、非常に遅くて駄目なコードに大きな間違いがありますか?
EDIT:
あまりラグが、それでもだけでなくそれが必要として実行していないnotifyItemRangeChanged(pos, pos);
への道をnotifyItemChanged(pos);
を変更した後。
コードをシンプルにするために、自動スクロールを担当していたすべてのもの(上記のライブラリの機能)も削除しました。ここで
私Fragment
public class TestFragment extends Fragment
{
@BindView(R.id.gridView_hours) GridView gridView_hours;
@BindView(R.id.weekdays_container) LinearLayout weekdays_container;
private String[] hours = new String[]{"00:00", "01:00", "02:00", "03:00", "04:00", "05:00", "06:00", "07:00", "08:00", "09:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00", "19:00", "20:00", "21:00", "22:00", "23:00"};
private String[] hoursShort = new String[]{"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"};
@Override
public void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_test, container, false);
ButterKnife.bind(this, view);
for (int i = 1; i <= 7; i++)
{
RecyclerView recyclerView = new RecyclerView(getActivity());
initAdapter(recyclerView);
GridLayoutManager glm = new GridLayoutManager(getActivity(), 48, GridLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(glm);
weekdays_container.addView(recyclerView);
}
gridView_hours.setAdapter(new ArrayAdapter<String>(getActivity(), R.layout.hour_view, hoursShort));
// GridLayoutManager glm = new GridLayoutManager(getActivity(), 48, GridLayoutManager.VERTICAL, false);
// recyclerView.setLayoutManager(glm);
return view;
}
private void initAdapter(RecyclerView recyclerView)
{
TestAutoDataAdapter adapter = new TestAutoDataAdapter(getActivity(), 48);
recyclerView.setAdapter(adapter);
DragSelectionProcessor dragSelectionProcessor = new DragSelectionProcessor(new DragSelectionProcessor.ISelectionHandler() {
@Override
public HashSet<Integer> getSelection() {
return adapter.getSelection();
}
@Override
public boolean isSelected(int index) {
return adapter.getSelection().contains(index);
}
@Override
public void updateSelection(int start, int end, boolean isSelected, boolean calledFromOnStart) {
adapter.selectRange(start, end, isSelected);
}
});
DragSelectTouchListener dragSelectTouchListener = new DragSelectTouchListener()
.withSelectListener(dragSelectionProcessor);
recyclerView.addOnItemTouchListener(dragSelectTouchListener);
adapter.setClickListener(new TestAutoDataAdapter.ItemClickListener()
{
@Override
public void onItemClick(View view, int position)
{
adapter.toggleSelection(position);
}
@Override
public boolean onItemLongClick(View view, int position)
{
dragSelectTouchListener.startDragSelection(position);
return true;
}
});
}
}
とRecyclerView.Adapter
public class TestAutoDataAdapter extends RecyclerView.Adapter<TestAutoDataAdapter.ViewHolder>
{
private int dataSize;
private Context context;
private ItemClickListener clickListener;
private HashSet<Integer> selected;
public TestAutoDataAdapter(Context context, int size)
{
this.context = context;
dataSize = size;
selected = new HashSet<>();
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(context).inflate(R.layout.test_cell, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position)
{
holder.tvText.setText("");
if (selected.contains(position))
holder.tvText.setBackgroundColor(Color.RED);
else
holder.tvText.setBackgroundColor(Color.WHITE);
}
@Override
public int getItemCount()
{
return dataSize;
}
// ----------------------
// Selection
// ----------------------
public void toggleSelection(int pos)
{
if (selected.contains(pos))
selected.remove(pos);
else
selected.add(pos);
notifyItemChanged(pos);
}
public void select(int pos, boolean selected)
{
if (selected)
this.selected.add(pos);
else
this.selected.remove(pos);
notifyItemRangeChanged(pos, pos);
}
public void selectRange(int start, int end, boolean selected)
{
for (int i = start; i <= end; i++)
{
if (selected)
this.selected.add(i);
else
this.selected.remove(i);
}
notifyItemRangeChanged(start, end - start + 1);
}
public void deselectAll()
{
// this is not beautiful...
selected.clear();
notifyDataSetChanged();
}
public void selectAll()
{
for (int i = 0; i < dataSize; i++)
selected.add(i);
notifyDataSetChanged();
}
public int getCountSelected()
{
return selected.size();
}
public HashSet<Integer> getSelection()
{
return selected;
}
// ----------------------
// Click Listener
// ----------------------
public void setClickListener(ItemClickListener itemClickListener)
{
clickListener = itemClickListener;
}
public interface ItemClickListener
{
void onItemClick(View view, int position);
boolean onItemLongClick(View view, int position);
}
// ----------------------
// ViewHolder
// ----------------------
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener
{
public TextView tvText;
public ViewHolder(View itemView)
{
super(itemView);
tvText = itemView.findViewById(R.id.tvText);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
@Override
public void onClick(View view)
{
if (clickListener != null)
clickListener.onItemClick(view, getAdapterPosition());
}
@Override
public boolean onLongClick(View view)
{
if (clickListener != null)
return clickListener.onItemLongClick(view, getAdapterPosition());
return false;
}
}
}
SelectTouchListener
public class DragSelectTouchListener implements RecyclerView.OnItemTouchListener
{
private static final String TAG = "DSTL";
private boolean mIsActive;
private int mStart, mEnd;
private int mLastStart, mLastEnd;
private OnDragSelectListener mSelectListener;
public DragSelectTouchListener()
{
reset();
}
/**
* sets the listener
* <p>
*
* @param selectListener the listener that will be notified when items are (un)selected
*/
public DragSelectTouchListener withSelectListener(OnDragSelectListener selectListener)
{
this.mSelectListener = selectListener;
return this;
}
// -----------------------
// Main functions
// -----------------------
/**
* start the drag selection
* <p>
*
* @param position the index of the first selected item
*/
public void startDragSelection(int position)
{
setIsActive(true);
mStart = position;
mEnd = position;
mLastStart = position;
mLastEnd = position;
if (mSelectListener != null && mSelectListener instanceof OnAdvancedDragSelectListener)
((OnAdvancedDragSelectListener)mSelectListener).onSelectionStarted(position);
}
// -----------------------
// Functions
// -----------------------
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e)
{
if (!mIsActive || rv.getAdapter().getItemCount() == 0)
return false;
int action = e.getAction();
switch (action)
{
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_DOWN:
reset();
break;
}
return true;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e)
{
if (!mIsActive)
return;
int action = e.getAction();
switch (action)
{
case MotionEvent.ACTION_MOVE:
updateSelectedRange(rv, e);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
reset();
break;
}
}
private void updateSelectedRange(RecyclerView rv, MotionEvent e)
{
updateSelectedRange(rv, e.getX(), e.getY());
}
private void updateSelectedRange(RecyclerView rv, float x, float y)
{
View child = rv.findChildViewUnder(x, y);
if (child != null)
{
int position = rv.getChildAdapterPosition(child);
if (position != RecyclerView.NO_POSITION && mEnd != position)
{
mEnd = position;
notifySelectRangeChange();
}
}
}
private void notifySelectRangeChange()
{
if (mSelectListener == null)
return;
if (mStart == RecyclerView.NO_POSITION || mEnd == RecyclerView.NO_POSITION)
return;
int newStart, newEnd;
newStart = Math.min(mStart, mEnd);
newEnd = Math.max(mStart, mEnd);
if (mLastStart == RecyclerView.NO_POSITION || mLastEnd == RecyclerView.NO_POSITION)
{
if (newEnd - newStart == 1)
mSelectListener.onSelectChange(newStart, newStart, true);
else
mSelectListener.onSelectChange(newStart, newEnd, true);
}
else
{
if (newStart > mLastStart)
mSelectListener.onSelectChange(mLastStart, newStart - 1, false);
else if (newStart < mLastStart)
mSelectListener.onSelectChange(newStart, mLastStart - 1, true);
if (newEnd > mLastEnd)
mSelectListener.onSelectChange(mLastEnd + 1, newEnd, true);
else if (newEnd < mLastEnd)
mSelectListener.onSelectChange(newEnd + 1, mLastEnd, false);
}
mLastStart = newStart;
mLastEnd = newEnd;
}
private void reset()
{
setIsActive(false);
if (mSelectListener != null && mSelectListener instanceof OnAdvancedDragSelectListener)
((OnAdvancedDragSelectListener)mSelectListener).onSelectionFinished(mEnd);
mStart = RecyclerView.NO_POSITION;
mEnd = RecyclerView.NO_POSITION;
mLastStart = RecyclerView.NO_POSITION;
mLastEnd = RecyclerView.NO_POSITION;
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)
{
// ignore
}
public void setIsActive(boolean isActive)
{
this.mIsActive = isActive;
}
// -----------------------
// Interfaces and simple default implementations
// -----------------------
public interface OnAdvancedDragSelectListener extends OnDragSelectListener
{
/**
* @param start the item on which the drag selection was started at
*/
void onSelectionStarted(int start);
/**
* @param end the item on which the drag selection was finished at
*/
void onSelectionFinished(int end);
}
public interface OnDragSelectListener
{
/**
* @param start the newly (un)selected range start
* @param end the newly (un)selected range end
* @param isSelected true, it range got selected, false if not
*/
void onSelectChange(int start, int end, boolean isSelected);
}
}
のソースと必要なインターフェイスの実装
public class DragSelectionProcessor implements DragSelectTouchListener.OnAdvancedDragSelectListener {
private ISelectionHandler mSelectionHandler;
private HashSet<Integer> mOriginalSelection;
private boolean mFirstWasSelected;
private boolean mCheckSelectionState = false;
public DragSelectionProcessor(ISelectionHandler selectionHandler)
{
mSelectionHandler = selectionHandler;
}
@Override
public void onSelectionStarted(int start)
{
mOriginalSelection = new HashSet<>();
Set<Integer> selected = mSelectionHandler.getSelection();
if (selected != null)
mOriginalSelection.addAll(selected);
mFirstWasSelected = mOriginalSelection.contains(start);
mSelectionHandler.updateSelection(start, start, !mFirstWasSelected, true);
}
@Override
public void onSelectionFinished(int end)
{
mOriginalSelection = null;
}
@Override
public void onSelectChange(int start, int end, boolean isSelected)
{
for (int i = start; i <= end; i++)
checkedUpdateSelection(i, i, isSelected ? !mFirstWasSelected : mOriginalSelection.contains(i));
}
private void checkedUpdateSelection(int start, int end, boolean newSelectionState)
{
if (mCheckSelectionState)
{
for (int i = start; i <= end; i++)
{
if (mSelectionHandler.isSelected(i) != newSelectionState)
mSelectionHandler.updateSelection(i, i, newSelectionState, false);
}
}
else
mSelectionHandler.updateSelection(start, end, newSelectionState, false);
}
public interface ISelectionHandler
{
Set<Integer> getSelection();
boolean isSelected(int index);
void updateSelection(int start, int end, boolean isSelected, boolean calledFromOnStart);
}
}
あなたのスクリーンショットは壊れています。 – ziLk
両方の画像はまだ私のために働いています。キャッシュをクリアした後でも – woodii