마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

안드로이드 파이어베이스 검색기능 구현 RecyclerView 오류

0 추천

안드로이드와 파이어베이스 연결하여 바코드 촬영 시, 해당 바코드 내용을 출력하는 코드를 작성하고 있습니다. 코드상의 밑줄은 없지만 E/RecyclerView: No adapter attached; skipping layout 오류가 나옵니다 어떻게 수정해야할지 알려주세요 ㅠㅠ

ScanItemsActivity.java

public class ScanItemsActivity extends AppCompatActivity {
    public static EditText resultsearcheview;
    private FirebaseAuth firebaseAuth;
    ImageButton scantosearch;
    Button searchbtn;
    RecyclerView mrecyclerview;
    DatabaseReference mdatabaseReference;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scan_items);
        firebaseAuth = FirebaseAuth.getInstance();
        mdatabaseReference= FirebaseDatabase.getInstance().getReference("Items");
        resultsearcheview = findViewById(R.id.searchfield);
        scantosearch = findViewById(R.id.imageButtonsearch);
        searchbtn = findViewById(R.id.searchbtnn);
        mrecyclerview = findViewById(R.id.recyclerViews);
        LinearLayoutManager manager = new LinearLayoutManager(this);
        mrecyclerview.setLayoutManager(manager);
        mrecyclerview.setHasFixedSize(true);
        mrecyclerview.setLayoutManager(new LinearLayoutManager(this));
        scantosearch.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(getApplicationContext(), ScanCodeActivitysearch.class));
            }
        });
        searchbtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String searchtext = resultsearcheview.getText().toString();
                firebasesearch(searchtext);
            }
        });
    }
    private void firebasesearch(String searchtext) {
        Query firebaseSearchQuery = mdatabaseReference.orderByChild("itembarcode").startAt(searchtext).endAt(searchtext + "\uf8ff");
        FirebaseRecyclerOptions<Items> options =
                new FirebaseRecyclerOptions.Builder<Items>()
                        .setQuery(firebaseSearchQuery, Items.class)
                        .build();
        FirebaseRecyclerAdapter firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Items, UsersViewHolder>(options) {
            @Override
            protected void onBindViewHolder(@NonNull UsersViewHolder viewHolder, int position, @NonNull Items model) {
                viewHolder.setDetails(getApplicationContext(), model.getItembarcode(), model.getItemcategory(), model.getItemname(), model.getItemprice());
            }
            @Override
            public UsersViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
                View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_layout,parent,false);
                return new UsersViewHolder(view);
            }
        };
        mrecyclerview.setAdapter(firebaseRecyclerAdapter);
        firebaseRecyclerAdapter.startListening();
    }
    public static class UsersViewHolder extends RecyclerView.ViewHolder {
        View mView;
        public UsersViewHolder(View itemView) {
            super(itemView);
            mView = itemView;
        }
        public void setDetails(Context ctx, String itembarcode, String itemcategory, String itemname, String itemprice) {
            TextView item_barcode = (TextView) mView.findViewById(R.id.viewitembarcode);
            TextView item_name = (TextView) mView.findViewById(R.id.viewitemname);
            TextView item_category = (TextView) mView.findViewById(R.id.viewitemcategory);
            TextView item_price = (TextView) mView.findViewById(R.id.viewitemprice);
            item_barcode.setText(itembarcode);
            item_category.setText(itemcategory);
            item_name.setText(itemname);
            item_price.setText(itemprice);
       }
    }
}

list_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <TextView
        android:id="@+id/viewitembarcodename"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Item Barcode"/>
    <TextView
        android:id="@+id/viewitembarcode"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_toEndOf="@+id/viewitembarcodename"
        android:text="TextView" />
    <TextView
        android:id="@+id/viewitemnamename"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Item Name" />
    <TextView
        android:id="@+id/viewitemname"
        android:layout_toEndOf="@+id/viewitemnamename"
        android:text="TextView" />
    <TextView
        android:id="@+id/viewitempricename"
        android:text="Item Price $" />
    <TextView
        android:id="@+id/viewitemprice"
        android:layout_toEndOf="@+id/viewitempricename"
        android:text="TextView" />
    <TextView
        android:id="@+id/viewitemcategoryname"
        android:text="Item Category" />
    <TextView
        android:id="@+id/viewitemcategory"
        android:layout_toEndOf="@+id/viewitemcategoryname"
        android:text="TextView" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
<activity_scanitems.xml>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Barcode.ScanItemsActivity"
    android:orientation="vertical">
    <TextView
        android:id="@+id/textView"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="ScanItems"/>
    <LinearLayout
        android:orientation="vertical"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Button
            android:id="@+id/imageButtonsearch"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="바코드 스캔하기"
            android:layout_gravity="center"
            tools:ignore="VectorDrawableCompat" />
        <EditText
            android:id="@+id/searchfield"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ems="10"
            android:hint="Search Items"
            android:inputType="textPersonName"
            android:textAlignment="center"
            android:textColor="#fff"
            android:textColorHint="#F7F2F2"/>
        <Button
            android:layout_marginTop="10dp"
            android:layout_width="150dp"
            android:id="@+id/searchbtnn"
            android:layout_gravity="center"
            android:text="검색하기"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="200dp">
        <androidx.recyclerview.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/recyclerViews">
        </androidx.recyclerview.widget.RecyclerView>
    </LinearLayout>
</LinearLayout>
am9911 (160 포인트) 님이 2020년 10월 28일 질문
am9911님이 2020년 10월 28일 수정

1개의 답변

0 추천

우선 firebase에서 데이터를 가져오는 부분은 콜백을 쓰긴하지만 IO 관련된 작업이므로 백그라운드 쓰레드에서 실행하시기를 강력하게 권장드립니다. 메인쓰레드를 버겁게 하지 마세요. skipping layout 이란 메세지는 메인 쓰레드에서 너무 많은 일을 렌더링을 좀 건너띨게요 하는 메세지입니다. 퍼포먼스에 일단 문제가 있는 것으로 보이구요.

그리고 다른 한가지는 

firebaseRecyclerAdapter.startListening();

이 코드가 좀 의심스럽네요. 코드만 봐서는 파이어베이스의 변경이 있을 때 adapter내부에서 뭔가를 하는 것으로 보이는데, 바른 구조는 파이어베이스에서 데이터가 변경되면 액티비티에 어댑터에 해당 데이터를 다시 설정하는 것입니다. 그리고 화면에 보여지는 아이템이 많은 편이라면 ListAdapter를 사용하시길 권장합니다. 변경사항을 체크해서 변경된 아이템만 다시 바인딩을 해줍니다.

        mrecyclerview = findViewById(R.id.recyclerViews);
        LinearLayoutManager manager = new LinearLayoutManager(this);
        mrecyclerview.setLayoutManager(manager);
        mrecyclerview.setHasFixedSize(true);
        mrecyclerview.setLayoutManager(new LinearLayoutManager(this))

        // 아래처럼 수정하셔야 할 듯.
        recyclerView = findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setHasFixedSize(true);

recyclerView라는 변수이름의 역할에 맞게 알기쉬운 이름으로 바꿔주시길.

 

spark (227,530 포인트) 님이 2020년 10월 28일 답변
firebaseRecyclerAdapter.startListening(); 코드는 삭제했습니다!
위에 activity_scanItems.xml 파일을 잘못 올려 수정했습니다 ㅠㅠㅠ recyclerViews가 scanItems.xml 파일의 Recyclerview 아이디라 불러와서 mrecyclererview라는 변수에 넣어준 코드인데 이것이 잘못된걸까요..?
아니오 잘못된 것이 아니고 Java naming convention과 Code readability 때문입니다. 변수앞에 'm'은 아무의미가 없기 때문에 코드를 읽을 때 머릿속에서 제거를 하고 recyclerView로 바꿔어서 읽어야 하기 때문에 안붙이는게 낫습니다. 그리고 m 을 붙이시려면 camelCase로 쓰셔야 하구요.
에러를 정확하게 분석하려면 디버깅 모드로 가셔서 어느라인에서 어떤 에러메세지가 나오는지 알아야 겠죠. 만약
recyclerview.setAdapter(firebaseRecyclerAdapter);
부분이 문제라면, 이 코드를 onCreate 쪽으로 옮겨보세요. 그리고  Firebase 콜백에서
firebaseAdapter.setItems(newItems);
firebaseAdapter.notifyDatasetChanged();
처럼 데이터만 갱신해 주시구요.
...