简体   繁体   中英

RecyclerView has no layout manager

I've seen lots of these questions, but I don't have any child elements so I don't know why this isn't working properly. I moved this to a fragment from an activity and it's no longer working. I saw previous questions have issues with having child elements from the recyclerview but that's not the case here. Here's my code:

public class TodoListFragment extends Fragment {

    @BindView(R.id.todo_recycler_view)
    protected RecyclerView mRecyclerView;

    @BindView(R.id.fab)
    protected FloatingActionButton mFab;

    private TodoListAdapter mTodoListAdapter;
    private RecyclerView.LayoutManager mLayoutManager;

    public static TodoListFragment newInstance() {
        return new TodoListFragment();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.todo_list_recycler_view, container, false);

        ButterKnife.bind(this, view);
        mRecyclerView.setHasFixedSize(true);

        mLayoutManager = new LinearLayoutManager(getActivity());
        mRecyclerView.setLayoutManager(mLayoutManager);

        mTodoListAdapter = new TodoListAdapter();
        mRecyclerView.setAdapter(mTodoListAdapter);


        return view;
    }

    @OnClick(R.id.fab)
    public void onAdd() {
        TodoItemView todoItemView = new TodoItemView(getContext());
        todoItemView.setText("Schedule a dentist's appointment");
        todoItemView.setTime(new Date());
        mTodoListAdapter.addItem(todoItemView);
    }
}

And here's the todo_list_recycler_view.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
                                        xmlns:app="http://schemas.android.com/apk/res-auto"
                                        xmlns:tools="http://schemas.android.com/tools"
                                        android:id="@+id/todo_recycler_view"
                                        android:scrollbars="vertical"
                                        android:layout_width="match_parent"
                                        android:layout_height="match_parent"
                                        app:layout_behavior="@string/appbar_scrolling_view_behavior"
                                        tools:context=".presentation.TodoActivity"
                                        tools:showIn="@layout/activity_todo_list" />

Any idea why I'm getting this error?

Caused by: java.lang.IllegalStateException: RecyclerView has no LayoutManager
                                                                                    at android.support.v7.widget.RecyclerView.generateLayoutParams(RecyclerView.java:3393)
                                                                                    at android.view.LayoutInflater.inflate(LayoutInflater.java:502)
                                                                                    at android.view.LayoutInflater.inflate(LayoutInflater.java:423) 
                                                                                    at com.vorple.shortlist.shortlist.presentation.TodoListFragment.onCreateView(TodoListFragment.java:44)

Consider moving this line

mRecyclerView.setHasFixedSize(true);

after the line where you assign the layout manager to the recycler view. You may need the layout manager declared before you can call setHasFixedSize

尝试在另一个方法重写中添加recyclerview管理器,Butterknife和对象可能正在工作,将布局管理器添加到onResume中或需要加载数据时会发生什么情况。

I am very confuse with what you want to achieve with TodoItemView ( link ) class. Inside init() method, you are inflating another view which I assumed you want to use this class to display the item in list. The problem arise inside this class which, based on your logic, every time you add a model, you will re-inflate the view. It supposed to be inflated once, then store the reference and simply grab back the view when you add another item. In this case, I don't think recyclerView capable to recycle your view.

So, I have recreated a scenario and simplified your code. But I am not sure if this is the exact solution of your problem. But, maybe you can find something useful here.

1) Activity

public class MainActivity extends AppCompatActivity{

private Button playBtn, startActivityBtn;
private MediaPlayer mediaPlayer;

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

    getSupportFragmentManager().beginTransaction()
            .add(R.id.mainContent, new TododFragment())
            .commit();

}
}

2) Fragment

public class TododFragment extends Fragment {

private RecyclerView mRecyclerView;
private RecyclerView.LayoutManager mLayoutManager;
private TodoListAdapter mTodoListAdapter;

public TododFragment() {
    // Required empty public constructor
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_todod, container, false);
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    mRecyclerView = (RecyclerView)view.findViewById(R.id.todo_recycler_view);
    mRecyclerView.setHasFixedSize(true);

    mLayoutManager = new LinearLayoutManager(getActivity());
    mRecyclerView.setLayoutManager(mLayoutManager);

    mTodoListAdapter = new TodoListAdapter();
    mRecyclerView.setAdapter(mTodoListAdapter);

    //Create 10 dummies data
    SimpleDateFormat dt = new SimpleDateFormat("h:mm a");
    for(int i = 0; i < 10; i++){
        if(i % 2 == 0)
            mTodoListAdapter.addItem(new TodoItem("Schedule a dentist's appointment", dt.format(new Date()), 0));
        else
            mTodoListAdapter.addItem(new TodoItem("Schedule a dentist's appointment", dt.format(new Date()), 1));
    }
}
}

3) Model

public class TodoItem {
private String text;
private String time;
//LayoutType is used to display different layout.
private int layoutType;

public TodoItem(String text, String time, int layoutType) {
    this.text = text;
    this.time = time;
    this.layoutType = layoutType;
}

public String getText() {
    return text;
}

public void setText(String text) {
    this.text = text;
}

public String getTime() {
    return time;
}

public void setTime(String time) {
    this.time = time;
}

public int getLayoutType() {
    return layoutType;
}

public void setLayoutType(int layoutType) {
    this.layoutType = layoutType;
}
}

4) Adapter (In this case, I have two layout. If you want to use one layout, remove getItemViewType )

public class TodoListAdapter extends RecyclerView.Adapter<TodoListAdapter.TodoItemViewHolder> {
private List<TodoItem> mItems;


public static class TodoItemViewHolder extends RecyclerView.ViewHolder{

    public TextView mTextViewText;
    public TextView mTextViewTime;

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

        mTextViewText = (TextView)itemView.findViewById(R.id.mTextViewText);
        mTextViewTime = (TextView)itemView.findViewById(R.id.mTextViewTime);
    }
}

public TodoListAdapter() {
    mItems = new ArrayList<>();
}

@Override
public int getItemViewType(int position) {
    //ItemViewType will return an int for every row. You can use this to control with layout you want to display
    return mItems.get(position).getLayoutType();
}

@Override
public TodoItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    //get the viewType and display the layout accordingly
    View view = null;
    if(viewType == 0){
        view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.todo_list_item, parent, false);
    }
    else{
        view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.todo_list_item2, parent, false);
    }

    TodoItemViewHolder rcv = new TodoItemViewHolder(view);
    return rcv;
}

@Override
public void onBindViewHolder(TodoItemViewHolder holder, int position) {
    holder.mTextViewText.setText(mItems.get(position).getText());
    holder.mTextViewTime.setText(mItems.get(position).getTime());
}

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

public void addItem(TodoItem item) {
    mItems.add(item);
    notifyItemInserted(mItems.size() - 1);
}
}

6) Result 在此处输入图片说明

I figured out the cause of the exception.

Here is what I changed within my activity's XML file:

<!-- <include layout="@layout/todo_list_recycler_view" /> -->
    <FrameLayout
        android:id="@+id/todo_list_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

Instead of including @layout/todo_list_recycler_view in the activity's layout file, I just replaced the empty FrameLayout with todo_list_recycler_view when instantiating my fragment and that gets rid of the exception. I'm not sure why this is the case but it solves the issue for me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM