Kev*_*lis 4 android focus android-edittext android-recyclerview
我有一个应用程序,我用RecycleView用CardViews.该CardView含有EditText当我添加一个新的,现在CardView到RecycleView了EditText应该有重点,键盘应该会出现.
我怎样才能做到这一点?我试图在以下内容中添加一些代码onBindViewHolder:
public void onBindViewHolder(TodoViewHolder holder, final int position) {
...
if(holder.tvDescription.requestFocus()) {
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
}
...
}
Run Code Online (Sandbox Code Playgroud)
或者虽然创造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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是我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());
}
}
Run Code Online (Sandbox Code Playgroud)
实施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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
的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 + "]";
}
}
Run Code Online (Sandbox Code Playgroud)
并在MainActivityadd方法中:
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);
}
});
Run Code Online (Sandbox Code Playgroud)
测试某些解决方案时出现问
问题是因为你打电话requestFocus()太早,导致你的视图还没有出现在屏幕上.此外,当您添加新元素时,您应该添加一些标志 - 如果您请求关注此视图,则应防止所有先前的视图RecyclerView被聚焦.假设你添加了一个新CardView的结尾RecyclerView,所以你的add方法Adapter应该是这样的:
public void addToEnd(Model item) {
item.shouldBeFocused = true;
dataset.add(item);
notifyItemInserted(dataset.size() - 1);
}
Run Code Online (Sandbox Code Playgroud)
然后在你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;
}
...
Run Code Online (Sandbox Code Playgroud)
此外,您可能需要滚动到您的最后位置,RecyclerView以便onBindViewHolder()为新添加的元素调用.例如,您可以通过setStackFromEnd = true line 执行此操作.
更新:
你的问题是你正在添加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
}
}
Run Code Online (Sandbox Code Playgroud)
然后将它添加到你的EditTextin ViewHolder构造函数中,何时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());
}
}
Run Code Online (Sandbox Code Playgroud)
而最后,而不是添加新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);
...
}
Run Code Online (Sandbox Code Playgroud)
这应该像魅力一样!从这个完美的答案得到了解决方案,所以检查它的更多背景.
| 归档时间: |
|
| 查看次数: |
4726 次 |
| 最近记录: |