RecyclerView Drawer - 设置选择

RecyclerView Drawer - Set Selection

我正在尝试编写我自己的 recyclerview 选择器算法。到目前为止,除了处理第一个选择之外,我已经成功地编写了部分代码。基本上我想将所选项目的背景设置为蓝色,其他地方设置为白色(与 ListView 的 setSelection 相同)。

所以,想法是:

  1. 在 Adapter 的 onCreateViewHolder 方法中为第一个元素设置蓝色背景。
  2. 在ActivityMain中,定义一个实例View变量navMenuSelection来存储当前选择
  3. 在recyclerView的onclick监听器中,将点击视图的背景设置为蓝色,将navMenuSelection的背景设置为白色,并将navMenuSelection更新为点击视图

一切正常,除了: 1.无法用第一个视图初始化navMenuSelection。试图在 onPostCreate 方法中使用 mDrawerLayout.getChildAt(0),但是 returns null

  1. 如何在 savedInstanceState bundle 中传递视图 navMenuSelection?

任何想法将不胜感激。

public class ActivityMain extends AppCompatActivity {
    private View navMenuSelection = null; // Select current view

    protected void onCreate(Bundle savedInstanceState) {
    // ALL THE CODES TO DEFINE RECYCLERVIEW

        mRecyclerView.addOnItemTouchListener(new RecycleTouchListener(this, new ClickListner() {
            @Override
            public void onClick(View view, int position) {
                if(view != navMenuSelection){
                    setNavItemSelected(view);
                    removeNavItemSelected(navMenuSelection);
                    navMenuSelection = view;
                    mDrawerLayout.closeDrawers();
                }
            }
        }));
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        mDrawerToggle.syncState();
        navMenuSelection = mDrawerLayout.getChildAt(0);
    }
    @Override
    public void onSaveInstanceState(Bundle savedInstanceState){
        //savedInstanceState.put???("currselection", navMenuSelection); // HOW TO DO THAT
        super.onSaveInstanceState(savedInstanceState);
    }

    public void setNavItemSelected(View v){
        if(v!= null) {
            v.setBackgroundColor(R.color.BLUE));
        }
    }

    public void removeNavItemSelected(View v){
        if(v!= null) {
            v.setBackgroundColor(R.color.WHITE));
        }
    }
}

适配器Class(将 onClick 事件移动到适配器后)

public class NavDrawerAdapter extends RecyclerView.Adapter<NavDrawerAdapter.ViewHolder> {

    private String[] mNavTitles; // stores title
    private int[] mIcons; // stores icon

    private Context context;
    private int oldpostion = 0;

    public NavDrawerAdapter(Context context, String Titles[], int[] Icons){
        this.context = context;
        mNavTitles = Titles;
        mIcons = Icons;
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        TextView textView;
        ImageView imageView;

        public ViewHolder (View itemView) {
            super(itemView);
            textView = (TextView) itemView.findViewById(R.id.title);
            imageView = (ImageView) itemView.findViewById(R.id.icon);
        }
    }

    @Override
    public NavDrawerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.navdrawer_item,parent,false);
        return new ViewHolder(v,viewType);
    }

    @Override
    public void onBindViewHolder(NavDrawerAdapter.ViewHolder holder, final int position) {
        holder.textView.setText(mNavTitles[position]);
        holder.imageView.setImageResource(mIcons[position]);
        if(position == 0) {
            setNavItemSelected(holder.itemView);
        }
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position != oldpostion){
                    setNavItemSelected(v);
                    //removeNavItemSelected(OLD VIEW);
                    oldpostion = position;
                }
            }
        });
    }

    @Override
    public int getItemCount() {
        return mNavTitles.length;
    }

    @Override
    public int getItemViewType(int position) {
        return 1;
    }

    public void setNavItemSelected(View v){
        if(v!= null) {
            v.setBackgroundColor(context.getResources().getColor(R.color.navdrawer_item_selected_bg));
            TextView tview = (TextView) v.findViewById(R.id.title);
            tview.setTextColor(context.getResources().getColor(R.color.navdrawer_item_selected_text));
        }
    }

    public void removeNavItemSelected(View v){
        if(v!= null) {
            v.setBackgroundResource(R.drawable.list_selector_nav_drawer);
            TextView tview = (TextView) v.findViewById(R.id.title);
            tview.setTextColor(context.getResources().getColorStateList(R.color.list_selector_nav_drawer_text));
        }
    }
}

关于您的问题:

  1. 要在 RecyclerView 中获得您需要的视图,您可以使用 mRecyclerView.findViewHolderForAdapterPosition(position)
  2. 我可能错了,但我认为你只需要保存视图的位置,而不是视图本身,它只是 int

我之前使用的另一种模式是向您的 RecyclerView.Adapter 添加一个布尔数组,该数组保存有关当前应激活哪些视图(与数据集合中的位置相关联)的信息.基于这个数组,我正在 onBindViewHolder().

中更改视图的背景

如果回收站视图足够短,可以在单个屏幕上看到,则您当前的解决方案是可行的。但是一旦它开始回收它的旧视图持有者,你就会得到 "selected" 背景在未选择的视图上重用 - 这都是因为你记住了视图,而不是连接到它们的数据位置。

编辑

我会在这里放一些来自适配器的相关代码来说明我的实现想法。在我的方法中,我为 activated 状态使用了带有 "selected" 背景的可绘制状态列表,但是您可以通过手动设置背景来完成所有相同的操作。

public class RecyclerAdapter
        extends RecyclerView.Adapter<RecyclerAdapter.YourViewHolder> {

    private final List<...> data;
    private final RecyclerAdapterCallbacks listener; //e.g. for activity callbacks
    private final List<Boolean> activation;

    public RecyclerAdapter(List<...> data, RecyclerAdapterCallbacks listener) {
        this.data = data;
        this.listener = listener;
        this.activation = new ArrayList<>(data.size());
        fillActivationList();
    }

    private void fillActivationList() {
        int size = data.size();
        for (int i = 0; i < size; i++) activation.add(false);
    }

    //------YourViewHolder implementation here------

    public interface RecyclerAdapterCallbacks { //Callbacks interface if needed
        void onRowSelected(... dataPiece);

        void onRowDeselected();
    }

    @Override
    public WordsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //your code
    }

    @Override
    public void onBindViewHolder(YourViewHolder holder, final int position) {
        holder.field.setText(data.get(position).getString());

        holder.itemView.setActivated(activation.get(position));
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Boolean previousState = activation.get(position);
                deactivateAll();
                activation.set(position, !previousState);
                notifyDataSetChanged();
                if (!previousState) {
                    listener.onRowSelected(data.get(position));
                } else {
                    listener.onRowDeselected();
                }
            }
        });
    }

    private void deactivateAll() {
        Collections.fill(activation, false);
    }

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

    public void clearResults() {
        notifyItemRangeRemoved(0, data.size());
        data.clear();
        activation.clear();
    }

    public void add(Word item) {
        data.add(item);
        notifyItemInserted(data.size() - 1);
        activation.add(false);
    }

由于您已经使用数组来传递您的图标和标题,让我们添加另一个数组来保存您当前的选择

 private boolean isSelected[] = {true, false, false, false, false};   //make sure that the length of this array matches the length of navtitles and navicon. set the first element to true since you want the first item to be selected by default. you can declare this directly on the adapter if this is static

或者您可以将其作为参数传递

private boolean isSelected[];
public NavDrawerAdapter(Context context, String Titles[], int[] Icons, boolean[] isSelected){
    this.context = context;
    mNavTitles = Titles;
    mIcons = Icons;
    this.isSelected = isSelected;
}

....


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

    holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            for(int i=0; i < isSelected.length(); i++){
                if (i==position){
                    isSelected[position]=true;
                else
                    isSelected[position]=false;
            }
            notifyDataSetChanged();
        }
    });

    if (isSelected[position]==true){
        //execute your codes here
    }else{
        //execute your codes here

    }
}

如果有任何错误或拼写错误,请尝试调试。我没有为此使用任何代码编辑器,所以可能会有一些错误

为了让您更轻松,我建议您只在取景器中添加布局,这样您就可以只更改布局的背景。不是最干净的方法,但这个应该可以解决问题

public class ViewHolder extends RecyclerView.ViewHolder {

    TextView textView;
    ImageView imageView;
    LinearLayout layout; //whatever layout your using, just an example

    public ViewHolder (View itemView) {
        super(itemView);
        textView = (TextView) itemView.findViewById(R.id.title);
        imageView = (ImageView) itemView.findViewById(R.id.icon);
        layout = (LinearLayout) itemView.findViewById(R.id.layout);
    }
}

...

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

holder.itemView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        for(int i=0; i < isSelected.length(); i++){
            if (i==position){
                isSelected[position]=true;
            else
                isSelected[position]=false;
        }
        notifyDataSetChanged();
    }
});

if (isSelected[position]==true){
    //execute your codes here
     holder.layout.setBackgroundResource(R.drawable.list_selector_nav_drawer);          
     holder.textView.setTextColor(context.getResources().getColorStateList(R.color.list_selector_nav_drawer_text));

}else{
    //execute your codes here
     holder.layout.setBackgroundResource(R.drawable.list_selector_nav_drawer);
     holder.textView.setTextColor(context.getResources().getColorStateList(R.color.list_selector_nav_drawer_text));

}
}