2017-10-30 25 views
1

私は現在、毎週の時間枠を編集するためのアプリを実現しようとしています。 ソケットのメカニカルタイマーと似ていますが、私の場合は毎日毎日です。選択可能なRecyclerViewパフォーマンスの問題

enter image description here

粒度は(私はそれが15又は30分であろう推測)二次最初の場所にあります。

私のアプローチはRecyclerViewであり、GridLayoutManagerArrayAdapterで、すべてのセルに項目がありました。

さらに多くのセルを選択するには、セルを長押しして他のセルにドラッグすることができます。これを達成するために、私は以下の図書館DragSelectRecyclerViewのリスナーを使用しました。

これはかなりうまく動作しますが、エミュレータや古い携帯電話では非常に遅くて遅いですが、非常に良い項目を選択できます。デバッグログカットでは、ビューのレンダリング時に多数のフレームをスキップし、複数のセルを選択する必要があることも確認できます。

enter image description here

そのような行動を達成するためのより良い方法はあります。それとも、非常に遅くて駄目なコードに大きな間違いがありますか?

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); 
     } 
    } 
+0

あなたのスクリーンショットは壊れています。 – ziLk

+0

両方の画像はまだ私のために働いています。キャッシュをクリアした後でも – woodii

答えて

1

notifyItemRangeChanged(pos, pos);notifyDataSetChanged()などを各選択プロセスで使用しません。

なぜあなたはこれが好きではありませんか。このようにそのアダプタで

1.Tookあなたrecyclerview参照:この方法のようにあなたの全体の選択プロセスを変更

 public void select(int pos, boolean selected){ 
      // Get selected view holder from recyclerview 
      ViewHolder holder = recyclerview..findViewHolderForAdapterPosition(pos); 

      if (selected) 
       this.selected.add(pos); 
      else 
       this.selected.remove(pos); 
      //notifyItemRangeChanged(pos, pos); 
      holder.tvText.setBackgroundColor(selected ? Color.RED : Color.WHITE); 

     } 

を:の

private RecyclerView mRecyclerView; 
public TestAutoDataAdapter(Context context, int size, RecyclerView pRecyclerView){ 
     this.mRecyclerView = pRecyclerView; 
     .. 

2.Setの背景色このようviewholder選択結果を教えてください。

+0

はチャームのように機能します:) 私はそれを知っていました...私の問題のような簡単な解決策です.- あなたの提案を改善するためのメモ...私はむしろ 'onAttachedToRecyclerView'を使って'RecyclerView' – woodii

関連する問題