2016-07-14 2 views
2

У меня есть приложение, где я использую RecycleView с CardViews. CardView содержит EditText сейчас, когда я добавляю новый CardView в RecycleViewEditText должен быть сфокусирован, и клавиатура должна появиться.Focus EditText from add item в RecycleView

Как я могу это достичь? Я попытался добавить код в onBindViewHolder:

public void onBindViewHolder(TodoViewHolder holder, final int position) { 
    ... 
    if(holder.tvDescription.requestFocus()) { 
     window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); 
    } 
    ... 
} 

Или в то время как создание ViewHolder, но это не сработало.

public class TodoViewHolder extends RecyclerView.ViewHolder { 
    protected CheckBox cbDone; 
    protected EditText tvDescription; 
    protected 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); 

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

Вот мой AdapterCode раствор:

public abstract class ArrayAdapter<T, VH extends RecyclerView.ViewHolder> 
     extends RecyclerView.Adapter<VH> { 

    private static final String TAG = "CustomArrayAdapter"; 

    private List<T> mObjects; 

    public ArrayAdapter(final List<T> objects) { 
     mObjects = objects; 
    } 

    /** 
    * Adds the specified object at the end of the array. 
    * 
    * @param object The object to add at the end of the array. 
    */ 
    public void add(final T object) { 
     mObjects.add(object); 
     notifyItemInserted(getItemCount() - 1); 
    } 

    /** 
    * Remove all elements from the list. 
    */ 
    public void clear() { 
     final int size = getItemCount(); 
     mObjects.clear(); 
     notifyItemRangeRemoved(0, size); 
    } 

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

    public T getItem(final int position) { 
     return mObjects.get(position); 
    } 

    public long getItemId(final int position) { 
     return position; 
    } 

    public List<T> getItems() { 
     return mObjects; 
    } 

    /** 
    * Returns the position of the specified item in the array. 
    * 
    * @param item The item to retrieve the position of. 
    * @return The position of the specified item. 
    */ 
    public int getPosition(final T item) { 
     return mObjects.indexOf(item); 
    } 

    /** 
    * Inserts the specified object at the specified index in the array. 
    * 
    * @param object The object to insert into the array. 
    * @param index The index at which the object must be inserted. 
    */ 
    public void insert(final T object, int index) { 
     mObjects.add(index, object); 
     notifyItemInserted(index); 

    } 

    /** 
    * Removes the specified object from the array. 
    * 
    * @param object The object to remove. 
    */ 
    public void remove(T object) { 
     final int position = getPosition(object); 
     remove(position); 
    } 

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

    /** 
    * Sorts the content of this adapter using the specified comparator. 
    * 
    * @param comparator The comparator used to sort the objects contained in this adapter. 
    */ 
    public void sort(Comparator<? super T> comparator) { 
     Collections.sort(mObjects, comparator); 
     notifyItemRangeChanged(0, getItemCount()); 
    } 
} 

Внедренной Adapter:

public class RecyclerViewAdapter extends ArrayAdapter<Todo, RecyclerViewAdapter.TodoViewHolder> { 

    private static final String TAG = "RecyclerViewAdapter"; 
    private Todo selectedItem; 
    private final Window window; 

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

    public Todo getSelectedItem() { 
     return selectedItem; 
    } 

    public class TodoViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener { 
     protected CheckBox cbDone; 
     protected EditText tvDescription; 
     protected 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); 

      itemView.setOnCreateContextMenuListener(this); 
     } 

     @Override 
     public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { 
      menu.setHeaderTitle("Send to:"); 
      menu.add(0, v.getId(), 0, "all"); 

      Log.d(TAG, "view id: " + v.getId()); 
     } 
    } 

    @Override 
    public void add(Todo object) { 
     object.shouldBeFocused = true; 
     super.add(object); 
    } 

    @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(final TodoViewHolder holder, final int position) { 
     final Todo todo = getItem(holder.getAdapterPosition()); 
     holder.cbDone.setChecked(todo.isChecked); 
     holder.tvDescription.setText(todo.description); 

     holder.tvDescription.addTextChangedListener(new TextWatcher() { 
      @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) { 
       todo.description = s.toString(); 
      } 
     }); 

     holder.cbDone.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 
      @Override 
      public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 
       Log.i(TAG, "onCheckedChanged called: isDone=" + isChecked); 
       todo.isChecked = isChecked; 
      } 
     }); 

     holder.btnDelete.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       Log.i(TAG, "onClick called: remove todo."); 
       remove(todo); 
      } 
     }); 

     View.OnLongClickListener onClickListener = new View.OnLongClickListener() { 
      @Override 
      public boolean onLongClick(View v) { 
       selectedItem = todo; 
       return false; 
      } 
     }; 

     holder.cbDone.setOnLongClickListener(onClickListener); 
     holder.tvDescription.setOnLongClickListener(onClickListener); 
     holder.btnDelete.setOnLongClickListener(onClickListener); 

     if (todo.shouldBeFocused) { 
      holder.tvDescription.post(new Runnable() { 
       @Override 
       public void run() { 
        if (holder.tvDescription.requestFocus()) { 
         window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); 
         InputMethodManager inputMethodManager = (InputMethodManager) holder.tvDescription.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 
         inputMethodManager.showSoftInput(holder.tvDescription, InputMethodManager.SHOW_IMPLICIT); 
        } 
       } 
      }); 
      todo.shouldBeFocused = false; 
     } 
    } 
} 

Todo:

public class Todo implements Serializable { 

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

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

    public Date getCreationDate() { 
     return creationDate; 
    } 

    public String getDescription() { return description; } 

    @Override 
    public String toString() { 
     return creationDate + ": " + description + " state[" + isChecked + "]"; 
    } 
} 

й в методе MainActivity ДОБАВЛЯТЬ:

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

Проблема при тестировании какое-то решение:

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

+0

Как насчет использования 'holder.tvDescription.setSelection (position)'? Это работает? – Razor

+0

@Razor это через 'IndexOutOfBoundsException':/ –

ответ

3

Проблема заключается в том, потому что вы звоните requestFocus() слишком рано, ca использование вашего представления еще не появилось на экране. Также вы должны добавить некоторый флаг, когда вы добавляете новый элемент, если вы хотите сосредоточиться на этом представлении или нет, чтобы не сфокусировать все предыдущие представления в RecyclerView. Предполагая, что вы добавляете новый CardView к концу RecyclerView, таким образом метод добавить в Adapter должно быть так:

public void addToEnd(Model item) { 
    item.shouldBeFocused = true; 
    dataset.add(item); 
    notifyItemInserted(dataset.size() - 1); 
} 

И тогда в вашем onBindViewHolder() сделать что-то вроде этого:

Model item = dataset.get(position); 
... 
if (item.shouldBeFocused) { 
    holder.tvDescription.post(new Runnable() { 
     @Override 
     public void run() { 
      if (holder.tvDescription.requestFocus()) { 
       window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); 
       InputMethodManager inputMethodManager = (InputMethodManager) holder.tvDescription.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 
       inputMethodManager.showSoftInput(holder.tvDescription, InputMethodManager.SHOW_IMPLICIT); 
      } 
     } 
    }); 
    item.shouldBeFocused = false; 
} 
... 

Также вы вероятно, нужно прокрутить до последней позиции вашего RecyclerView, чтобы позвонить вашему onBindViewHolder() для нового добавленного элемента. Вы можете сделать это, например, по линии setStackFromEnd = true.

UPDATE:

Ваша проблема заключается в том, что вы добавляете TextWatcher внутри onBindViewHolder метод, во-первых, это очень дорогостоящая операция, а во-вторых, вы экономите введенный текст в final ссылки, вот почему ваш RecyclerView дает неприемлемое результаты после.

Итак, попробуйте создать свой пользовательский TextWatcher, который держит позицию текущего элемента в Adapterпервый:

private static class PositionTextWatcher implements TextWatcher { 
    private int position; 

    public void updatePosition(int position) { 
     this.position = position; 
    } 

    @Override 
    public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { 
     // no op 
    } 

    @Override 
    public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { 
     final Todo todo = getItem(position); 
     todo.description = charSequence.toString(); 
    } 

    @Override 
    public void afterTextChanged(Editable editable) { 
     // no op 
    } 
} 

Затем добавить его в свой EditText в конструкторе ViewHolder «s, когда onCreateViewHolder будет вызванный:

@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, new PositionTextWatcher()); 
} 

public class TodoViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener { 
    protected CheckBox cbDone; 
    protected EditText tvDescription; 
    protected FloatingActionButton btnDelete; 
    protected PositionTextWatcher positionTextWatcher; 

    public TodoViewHolder(View itemView, PositionTextWatcher positionTextWatcher) { 
     super(itemView); 

     cbDone = (CheckBox)itemView.findViewById(R.id.cbDone); 
     tvDescription = (EditText) itemView.findViewById(R.id.tvDescription); 
     btnDelete = (FloatingActionButton) itemView.findViewById(R.id.btnDelete); 
     this.positionTextWatcher = positionTextWatcher; 
     tvDescription.addTextChangedListener(this.positionTextWatcher); 
     itemView.setOnCreateContextMenuListener(this); 
    } 

    @Override 
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { 
     menu.setHeaderTitle("Send to:"); 
     menu.add(0, v.getId(), 0, "all"); 

     Log.d(TAG, "view id: " + v.getId()); 
    } 
} 

И , наконец, вместо добавления нового TextWatcher каждый раз, когда в onBindViewHolder(), просто обновить позицию в пользовательских TextWatcher:

@Override 
public void onBindViewHolder(final TodoViewHolder holder, final int position) { 
    final Todo todo = getItem(holder.getAdapterPosition()); 
    ... 
    holder.cbDone.setChecked(todo.isChecked); 
    holder.positionTextWatcher.updatePosition(position); 
    holder.tvDescription.setText(todo.description); 
    ... 
} 

Это должно работать как шарм! Получил это решение от this perfect answer, поэтому проверьте его на наличие дополнительного фона.

+0

фокусируется правильно, но проблема с отображением повторяющихся записей появляется, поскольку в «RecyclerView» больше элементов, поэтому требуется прокрутка. Я думаю, это зависит от вида переработки. –

+0

Извините, вас не достали. Не могли бы вы объяснить больше или сделать видео? Проблема с 'setStackFromEnd'? – rom4ek

+0

Я попробовал ваше решение и добавил изображения к вопросу, так как вы можете видеть, что некоторые записи дублируются (но они дублируются только в представлении, а не в «List»). –

0

Попробуйте так:

@Override 
public void onBindViewHolder(final ViewHolder holder, int position) { 

    ... 

    // If this ViewHolder needs keyboard focus, execute below code. 
    holder.itemView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 
     @Override 
     public void onGlobalLayout() { 
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 
       holder.itemView.getViewTreeObserver().removeOnGlobalLayoutListener(this); 
      } else { 
       holder.itemView.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
      } 

      EditText editText = holder.tvDescription; 
      editText.requestFocus(); 
      InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 
      imm.showSoftInput(editText, 0); 
     } 
    }); 
} 
+0

Это не работает, появляется проблема с' RecyclerView', некоторые записи отображаются несколько раз, а также 'EditText' не сфокусирован. –