2016-07-13 7 views
0

私はとCardLayoutRecyclerViewを使用し、CardLayoutは複数のコンポーネントを含んでいます。 RecyclerViewを記入するにはAdapterを使用しますが、私の問題は、新しいアイテムを追加するときに、アイテムが複数回、RecyclerViewに追加されることです。しかし、それは常に再現可能な動作ではありません(多分それはスレッディングと関係していますか?)。RecyclerViewは複数回商品を追加します

私は理由を知らない:/私はそれぞれのヒントやアドバイスにとてもうれしい。

以下のコードはTodoManagerとほぼ同じですが、唯一の問題は上で説明した問題です。

マイMainActivity

public class MainActivity extends AppCompatActivity { 

    private static final String TAG = "MainActivity"; 
    private static String fileName = "todos.ser"; 

    private ArrayList<Todo> todos; 
    private RecyclerViewAdapter adapter; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main_layout); 

     updateTodos(); 
     adapter = new RecyclerViewAdapter(todos, getWindow()); 

     final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.todos); 
     assert recyclerView != null; 
     final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); 
     recyclerView.setLayoutManager(linearLayoutManager); 
     recyclerView.setHasFixedSize(true); 
     recyclerView.setItemAnimator(new DefaultItemAnimator()); 
     recyclerView.setAdapter(adapter); 

     FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); 
     assert fab != null; 
     fab.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View view) { 
       adapter.addTodo(new Todo(""), todos.size()); 
       int count = adapter.getItemCount(); 
       recyclerView.smoothScrollToPosition(count - 1); 
      } 
     }); 
    } 

    private void updateTodos() { 
     todos = new ArrayList<>(); 
     todos.add(new Todo("")); 
    } 

    @Override 
    public void onStop() { 
     super.onStop(); 

     Log.i(TAG, "save todos=" + todos.size()); 
     // Save logic 
    } 
} 

Todo

public class Todo implements Serializable { 

    // The creationDate is not used at the moment 
    private Date creationDate; 
    private String description; 
    private boolean isChecked; 

    public Todo(String description) { 
     this.description = description; 
     this.creationDate = new Date(); 
    } 

    public String getDescription() { 
     return description; 
    } 

    public void setDescription(String description) { 
     this.description = description; 
    } 

    public boolean isChecked() { 
     return isChecked; 
    } 

    public void setIsChecked(boolean isChecked) { 
     this.isChecked = isChecked; 
    } 

    public Date getCreationDate() { 
     return creationDate; 
    } 
} 

RecyclerViewAdapter

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.TodoViewHolder> { 

    private static final String TAG = "RecyclerViewAdapter"; 

    private final List<Todo> todos; 
    private final Window window; 

    public RecyclerViewAdapter(List<Todo> todos, Window window) { 
     this.todos = todos; 
     this.window = window; 
    } 

    public class TodoViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, CompoundButton.OnCheckedChangeListener, TextWatcher { 
     CheckBox cbDone; 
     EditText tvDescription; 
     FloatingActionButton btnDelete; 

     public TodoViewHolder(View itemView) { 
      super(itemView); 

      cbDone = (CheckBox)itemView.findViewById(R.id.cbDone); 
      tvDescription = (EditText) itemView.findViewById(R.id.tvDescription); 
      btnDelete = (FloatingActionButton) itemView.findViewById(R.id.btnDelete); 

      tvDescription.addTextChangedListener(this); 
      cbDone.setOnCheckedChangeListener(this); 
      btnDelete.setOnClickListener(this); 
     } 

     @Override 
     public void onClick(View v) { 
      Log.i(TAG, "onClick called: remove todo."); 
      removeTodo(getAdapterPosition()); 
     } 

     @Override 
     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 
      Log.i(TAG, "onCheckedChanged called: isDone=" + isChecked); 
      Todo todo = getTodo(getAdapterPosition()); 
      todo.setIsChecked(isChecked); 
     } 

     @Override 
     public void beforeTextChanged(CharSequence s, int start, int count, int after) { 
      // Do nothing 
     } 

     @Override 
     public void onTextChanged(CharSequence s, int start, int before, int count) { 
      // Do nothing 
     } 

     @Override 
     public void afterTextChanged(Editable s) { 
      Log.i(TAG, "afterTextChanged called: text=" + s.toString()); 
      Todo todo = getTodo(getAdapterPosition()); 
      todo.setDescription(s.toString()); 
     } 
    } 

    public void addTodo(Todo todo, int position) { 
     if (position < 0 || position > todos.size()) { 
      Log.e(TAG, " add: index=" + position); 
     } else { 
      todos.add(position, todo); 
      notifyItemInserted(position); 
     } 
    } 

    public void removeTodo(int position) { 
     if (position < 0 || position >= todos.size()) { 
      Log.e(TAG, "remove: index=" + position); 
     } else { 
      todos.remove(position); 
      notifyItemRemoved(position); 
     } 
    } 

    public Todo getTodo(int position) { 
     Todo todo = null; 
     if (position < 0 || position >= todos.size()) { 
      Log.e(TAG, "get: index=" + position); 
     } else { 
      todo = todos.get(position); 
     } 
     return todo; 
    } 

    @Override 
    public TodoViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { 
     View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.todo_layout, viewGroup, false); 
     return new TodoViewHolder(view); 
    } 

    @Override 
    public void onBindViewHolder(TodoViewHolder holder, int position) { 
     holder.cbDone.setChecked(todos.get(position).isChecked()); 
     holder.tvDescription.setText(todos.get(position).getDescription()); 

     if(holder.tvDescription.requestFocus()) { 
      window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); 
     } 
    } 

    @Override 
    public int getItemCount() { 
     return todos.size(); 
    } 
} 

main_layout

CardViewため
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:background="@android:color/transparent"> 

    <RelativeLayout 
     android:id="@+id/header_layout" 
     android:layout_width="match_parent" 
     android:layout_height="@dimen/app_bar_height" 
     android:background="@color/colorPrimary" 
     android:layout_alignParentTop="true"> 

     <TextView 
      android:id="@+id/header" 
      android:layout_height="match_parent" 
      android:text="@string/todomanager" 
      android:textColor="@android:color/white" 
      android:layout_width="wrap_content" 
      android:layout_alignParentStart="true" 
      android:layout_marginStart="@dimen/margin_header_text" 
      android:textSize="@dimen/header_text_size" 
      android:gravity="center_vertical" /> 

     <android.support.design.widget.FloatingActionButton 
      android:id="@+id/fab" 
      android:layout_width="wrap_content" 
      android:layout_height="match_parent" 
      android:layout_margin="@dimen/margin_header_button" 
      app:backgroundTint="@color/colorAccent" 
      android:tint="@android:color/white" 
      app:fabSize="mini" 
      android:src="@android:drawable/ic_input_add" 
      android:layout_alignParentEnd="true" /> 
    </RelativeLayout> 

    <android.support.v7.widget.RecyclerView 
     android:id="@+id/todos" 
     android:layout_below="@id/header_layout" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:scrollbars="vertical" /> 

</RelativeLayout> 

レイアウト:

<android.support.v7.widget.CardView 
    xmlns:card_view="http://schemas.android.com/apk/res-auto" 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    android:id="@+id/todo" 
    android:layout_below="@id/header_layout" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    card_view:cardElevation="5dp" 
    android:layout_marginLeft="10dp" 
    android:layout_marginRight="10dp" 
    android:layout_marginBottom="5dp" 
    android:layout_marginTop="5dp"> 

    <RelativeLayout 
     android:layout_width="fill_parent" 
     android:layout_height="match_parent"> 

     <CheckBox 
      android:id="@+id/cbDone" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:layout_alignParentStart="true" 
      android:layout_centerVertical="true" /> 

     <EditText 
      android:id="@+id/tvDescription" 
      android:layout_width="fill_parent" 
      android:layout_height="wrap_content" 
      android:layout_toStartOf="@+id/btnDelete" 
      android:layout_toEndOf="@+id/cbDone" 
      android:hint="@string/insert_description" 
      android:background="@android:color/transparent" 
      android:padding="@dimen/padding_todo_text" 
      android:layout_centerVertical="true" 
      /> 

     <android.support.design.widget.FloatingActionButton 
      android:id="@+id/btnDelete" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:layout_margin="@dimen/margin_todo_button" 
      app:fabSize="mini" 
      app:backgroundTint="@color/colorAccent" 
      android:tint="@android:color/white" 
      android:src="@android:drawable/ic_menu_delete" 
      android:layout_alignParentEnd="true" 
      android:layout_centerVertical="true" /> 
    </RelativeLayout> 
</android.support.v7.widget.CardView> 

次のコードは、スタイリングのために必要とされる: dimens.xml

<resources> 
    <dimen name="app_bar_height">60dp</dimen> 
    <dimen name="fab_margin">16dp</dimen> 
    <dimen name="text_margin">16dp</dimen> 
    <dimen name="margin_header_text">10sp</dimen> 
    <dimen name="header_text_size">24sp</dimen> 
    <dimen name="margin_header_button">10sp</dimen> 
    <dimen name="padding_todo_text">10dp</dimen> 
    <dimen name="margin_todo_button">10sp</dimen> 
</resources> 

colors.xml

<resources> 
    <color name="colorPrimary">#212121</color> 
    <color name="colorPrimaryDark">#000000</color> 
    <color name="colorAccent">#009688</color> 
</resources> 

strings.xml

<resources> 
    <string name="todomanager">Todos</string> 
    <string name="insert_description">insert description ...</string> 
</resources> 

次のスクリーンショットは、間違った動作を示しています。左のものは正しい、中央のものは正しいが、スクロールが始まると、いくつかのエントリがビューに複製される。私はすべての重複エントリがまた消えたエントリの削除を一度クリックしたときに、私はそう、これらのエントリは、同じエントリであることを発見したいくつかのテストの後

Correct behavior at the beginning Still correct behavior Here you can see, that some entries are duplicated in the list

。そして、問題は私が表示のいくつかのエントリを持っている限りwhitスクロールを開始し、表示されたエントリの周りのスクロールが複製されます。

12番目の要素が追加される前のlogcat出力:

10アイテムを追加する際に最初の奇妙な行動を開始し
I/MainActivity: fab.onClick count=[7] 
I/RecyclerViewAdapter: afterTextChanged called: text= position=[6] 
I/RecyclerViewAdapter: afterTextChanged called: text=t position=[6] 
I/RecyclerViewAdapter: afterTextChanged called: text=te position=[6] 
I/RecyclerViewAdapter: afterTextChanged called: text=tes position=[6] 
I/RecyclerViewAdapter: afterTextChanged called: text=test position=[6] 
I/RecyclerViewAdapter: afterTextChanged called: text=test5 position=[6] 
I/RecyclerViewAdapter: addTodo position=[7] count=[7] 
I/MainActivity: fab.onClick count=[8] 
I/RecyclerViewAdapter: afterTextChanged called: text= position=[7] 
I/RecyclerViewAdapter: afterTextChanged called: text=t position=[7] 
I/RecyclerViewAdapter: afterTextChanged called: text=te position=[7] 
I/RecyclerViewAdapter: afterTextChanged called: text=tes position=[7] 
I/RecyclerViewAdapter: afterTextChanged called: text=test position=[7] 
I/RecyclerViewAdapter: afterTextChanged called: text=test6 position=[7] 
I/RecyclerViewAdapter: addTodo position=[8] count=[8] 
I/MainActivity: fab.onClick count=[9] 
I/RecyclerViewAdapter: afterTextChanged called: text= position=[8] 
I/RecyclerViewAdapter: afterTextChanged called: text=t position=[8] 
I/RecyclerViewAdapter: afterTextChanged called: text=te position=[8] 
I/RecyclerViewAdapter: afterTextChanged called: text=tes position=[8] 
I/RecyclerViewAdapter: afterTextChanged called: text=test position=[8] 
I/RecyclerViewAdapter: afterTextChanged called: text=test7 position=[8] 
I/RecyclerViewAdapter: addTodo position=[9] count=[9] 

、なぜなら

また、私は12日の要素を追加したときに、それが起動することを発見しましたそこにもafterTextChangedがあってはならない通話とGC無料です:

I/MainActivity: fab.onClick count=[10] 
I/RecyclerViewAdapter: afterTextChanged called: text= position=[9] 
I/RecyclerViewAdapter: afterTextChanged called: text=t position=[9] 
I/RecyclerViewAdapter: afterTextChanged called: text=te position=[9] 
I/RecyclerViewAdapter: afterTextChanged called: text=tes position=[9] 
I/RecyclerViewAdapter: afterTextChanged called: text=test position=[9] 
I/RecyclerViewAdapter: afterTextChanged called: text=test8 position=[9] 
I/RecyclerViewAdapter: afterTextChanged called: text=test3 position=[4] 
I/RecyclerViewAdapter: afterTextChanged called: text=test1 position=[2] 
I/RecyclerViewAdapter: afterTextChanged called: text=training position=[1] 
I/RecyclerViewAdapter: afterTextChanged called: text=work position=[0] 
I/art: Background partial concurrent mark sweep GC freed 52541(3MB) AllocSpace objects, 1(52KB) LOS objects, 35% free, 28MB/44MB, paused 7.486ms total 62.227ms 
I/RecyclerViewAdapter: addTodo position=[10] count=[10] 

は、11追加するときD 12項目afterTextChanged呼び出しが爆発すると奇妙な動作が開始されます:

I/MainActivity: fab.onClick count=[11] 
I/RecyclerViewAdapter: afterTextChanged called: text= position=[10] 
I/RecyclerViewAdapter: afterTextChanged called: text=t position=[10] 
I/RecyclerViewAdapter: afterTextChanged called: text=te position=[10] 
I/RecyclerViewAdapter: afterTextChanged called: text=tes position=[10] 
I/RecyclerViewAdapter: afterTextChanged called: text=test position=[10] 
I/RecyclerViewAdapter: afterTextChanged called: text=test9 position=[10] 
I/RecyclerViewAdapter: afterTextChanged called: text=test3 position=[4] 
I/RecyclerViewAdapter: afterTextChanged called: text=test1 position=[2] 
I/RecyclerViewAdapter: afterTextChanged called: text=training position=[1] 
I/RecyclerViewAdapter: afterTextChanged called: text=work position=[0] 
I/RecyclerViewAdapter: afterTextChanged called: text=work position=[0] 
I/RecyclerViewAdapter: afterTextChanged called: text=test7 position=[8] 
I/RecyclerViewAdapter: afterTextChanged called: text=test9 position=[10] 
I/RecyclerViewAdapter: afterTextChanged called: text=training position=[1] 
I/RecyclerViewAdapter: addTodo position=[11] count=[11] 
I/MainActivity: fab.onClick count=[12] 
I/RecyclerViewAdapter: afterTextChanged called: text= position=[11] 
I/RecyclerViewAdapter: afterTextChanged called: text=t position=[11] 
I/RecyclerViewAdapter: afterTextChanged called: text=te position=[11] 
I/RecyclerViewAdapter: afterTextChanged called: text=tes position=[11] 
I/RecyclerViewAdapter: afterTextChanged called: text=test position=[11] 
I/RecyclerViewAdapter: afterTextChanged called: text=test1 position=[11] 
I/RecyclerViewAdapter: afterTextChanged called: text=test10 position=[11] 
I/RecyclerViewAdapter: afterTextChanged called: text=test5 position=[6] 
I/RecyclerViewAdapter: afterTextChanged called: text=test3 position=[4] 
I/RecyclerViewAdapter: afterTextChanged called: text=test2 position=[3] 
I/RecyclerViewAdapter: afterTextChanged called: text=test1 position=[2] 
I/RecyclerViewAdapter: afterTextChanged called: text=training position=[1] 
I/RecyclerViewAdapter: afterTextChanged called: text=work position=[0] 
I/RecyclerViewAdapter: afterTextChanged called: text=work position=[0] 
I/RecyclerViewAdapter: afterTextChanged called: text=training position=[1] 
I/RecyclerViewAdapter: afterTextChanged called: text=work position=[0] 
I/RecyclerViewAdapter: afterTextChanged called: text=test6 position=[7] 
I/RecyclerViewAdapter: afterTextChanged called: text=test8 position=[9] 
I/RecyclerViewAdapter: afterTextChanged called: text=test9 position=[10] 
I/RecyclerViewAdapter: afterTextChanged called: text=test10 position=[11] 
I/RecyclerViewAdapter: afterTextChanged called: text=test10 position=[11] 
I/RecyclerViewAdapter: afterTextChanged called: text=test9 position=[10] 
I/RecyclerViewAdapter: afterTextChanged called: text=test10 position=[11] 
I/RecyclerViewAdapter: afterTextChanged called: text=test4 position=[5] 
I/RecyclerViewAdapter: afterTextChanged called: text=test2 position=[3] 
I/RecyclerViewAdapter: afterTextChanged called: text=test1 position=[2] 
I/RecyclerViewAdapter: afterTextChanged called: text=training position=[1] 
I/RecyclerViewAdapter: afterTextChanged called: text=work position=[0] 
I/RecyclerViewAdapter: afterTextChanged called: text=work position=[0] 
I/RecyclerViewAdapter: afterTextChanged called: text=training position=[1] 
I/RecyclerViewAdapter: afterTextChanged called: text=work position=[0] 
I/RecyclerViewAdapter: afterTextChanged called: text=test1 position=[2] 
I/RecyclerViewAdapter: afterTextChanged called: text=training position=[1] 
I/RecyclerViewAdapter: afterTextChanged called: text=work position=[0] 
I/RecyclerViewAdapter: afterTextChanged called: text=work position=[0] 

私が見つけた私のデータが正しいことであり、私が編集モードに入るときに、データが間違って見え、もう一つの奇妙な行動私が編集モードを終了するときもう一度大丈夫です。

expected entries duplicated entries in edit mode expected entries again

+0

ここで、updateTodos()を呼び出していますか。 ?複製された新しいデータはどうやって取得していますか? –

+0

@ZahidRasheed 'updateTodos()'は 'MainActivity'の' onCreate() 'で一度だけ呼び出されます。そこからファイルからデータをロードします(このコードは削除されましたが、コードははるかに大きくなります) –

+0

私はそのコードだけを調べることをお勧めします。私はRecyclerViewsとアダプタでの作業経験があり、アダプタソースが正しくなければ重複は起こりません。 –

答えて

1

問題は、フォーカスが設定されているので、私はフォーカスコードを削除すると、それが動作してonBindViewHolder方法にありました。

@Override 
public void onBindViewHolder(TodoViewHolder holder, int position) { 
    holder.cbDone.setChecked(todos.get(position).isChecked()); 
    holder.tvDescription.setText(todos.get(position).getDescription()); 

    // Remove the following code 
    // if(holder.tvDescription.requestFocus()) { 
    // window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); 
    // } 
} 
+0

ちょっと私はonBindHolderにフォーカスを設定していません。それでも同じ問題に直面しています。しかし、すべての重複した項目が消えてしまいます。 – Shruti

+0

試してみるためのコードがありますか? –

関連する問題