프로그래밍/Android

안드로이드 Recyclerview + php json 사용시 오류

소행성왕자 2021. 10. 21. 09:33

 

안드로이드에서 원격지의 json 파일 호출후

Recyclerview 를 이용하여 화면에 출력하려고 하는데

오류가 계속 발생합니다.

처음에는 onCreateViewHolder 메소드를 호출 안하더라구요.

아래 링크 참조하여 해결했습니다.

http://daplus.net/java-recyclerview%EA%B0%80-oncreateviewholder%EB%A5%BC-%ED%98%B8%EC%B6%9C%ED%95%98%EC%A7%80-%EC%95%8A%EC%8A%B5%EB%8B%88%EB%8B%A4/

 

[java] Recyclerview가 onCreateViewHolder를 호출하지 않습니다 - 리뷰나라

내는 RecyclerView호출하지 않습니다 onCreateViewHolder, onBindViewHolder도 MenuViewHolder에 따라서 아무것도 나타납니다, 생성자를 RecyclerView. 디버깅을 위해 로그를 넣고 로그가 표시되지 않습니다. 무엇이

daplus.net

 

그런데 또 다른 문제가 발생합니다.

E/RecyclerView: No adapter attached; skipping layout

..
..
..

D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.mycoupang, PID: 30655
    android.view.InflateException: Binary XML file line #16 in com.example.mycoupang:layout/card_layout_main: RecyclerView has no LayoutManager androidx.recyclerview.widget.RecyclerView{7745bc3 VFED..... ......I. 0,0-0,0 #7f070023 app:id/cardSiteCardView}, adapter:null, layout:null, context:com.example.mycoupang.MainActivity@152cecb
    Caused by: java.lang.IllegalStateException: RecyclerView has no LayoutManager androidx.recyclerview.widget.RecyclerView{7745bc3 VFED..... ......I. 0,0-0,0 #7f070023 app:id/cardSiteCardView}, adapter:null, layout:null, context:com.example.mycoupang.MainActivity@152cecb
        at androidx.recyclerview.widget.RecyclerView.generateLayoutParams(RecyclerView.java:4304)

 

이문제는 어댑터 또는 xml 에 문제가 있는것 같지만

아직도 해결을 못하고 있습니다.

MainActivity.java

package com.example.mycoupang;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.ImageView;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;


public class MainActivity extends AppCompatActivity {

    RecyclerView lecyclerView;
    ArrayList<CardForSite> list = new ArrayList<>();
    Handler handler;

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

        //세로모드
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        setContentView(R.layout.activity_main);
        getSupportActionBar().setTitle("좋아");

        /*
        final ImageView img1 = findViewById(R.id.imageView2);
        img1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setAction(Intent.ACTION_VIEW);
                intent.addCategory(Intent.CATEGORY_BROWSABLE);
                intent.setData(Uri.parse("http://zzz.com/more/?p=aHR0cHM6Ly9jb3VwYS5uZy9iNTFqQTk="));
                startActivity(intent);
            }
        });

         */

        /**
         * https://riucc.tistory.com/375
         */

        lecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        String url = "http://zzzz.com/android.php";

        NetworkTask networkTask = new NetworkTask(url, null);
        networkTask.execute();


        // 뷰를 띄우기 위한 구문(이렇게 안뜨면 아래로 방법으로)
        /*
        lecyclerView.setAdapter(new RecycleAdapter(list, R.layout.card_layout_main));
        lecyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
        lecyclerView.setItemAnimator(new DefaultItemAnimator());
        */


        // 위에껄로 하다보니 데이터를 많이 가져와 띄워주니 안뜨는 거를 볼 수 있었다
        // 핸들러를 통해 1초 이후에 뜨게 하니 정상적으로 잘 뜬다!!!

        handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                lecyclerView.setAdapter(new RecycleAdapter(list, R.layout.card_layout_main));
                System.out.println(">>>> aaa");
                lecyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
                System.out.println(">>>> bb");
                lecyclerView.setItemAnimator(new DefaultItemAnimator());
                System.out.println(">>>> cc");
            }
        }, 1000);   // 1000 = 1초 후 도출


    }

    public class NetworkTask extends AsyncTask<Void, Void, String> {

        private String url;
        private ContentValues values;

        public NetworkTask(String url, ContentValues values) {
            this.url = url;
            this.values = values;
        }

        @Override
        protected String doInBackground(Void... params) {
            String result; // 요청 결과를 저장할 변수.
            RequestHttpURLConnection requestHttpURLConnection = new RequestHttpURLConnection();
            result = requestHttpURLConnection.request(url, values);
            // 해당 URL로 부터 결과물을 얻어온다.
            return result;
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            //textView.setText(s);
        }
    }

    public class RequestHttpURLConnection {

        public String request(String _url, ContentValues _params) {
            // HttpURLConnection 참조 변수.
            HttpURLConnection urlConn = null;
            // URL 뒤에 붙여서 보낼 파라미터.
            StringBuffer sbParams = new StringBuffer();

            /**
             * 1. StringBuffer에 파라미터 연결
             * */

            // 보낼 데이터가 없으면 파라미터를 비운다.
            if (_params == null)
            // sbParams.append("");
            //  sbParams.append("miseID=" + miseID);
            //sbParams.append("&misePW=" + misePW);
            //sbParams.append("miseID=test001&misePW=test001&miseNAME=테스트001");
            //sbParams.append("memberID=test123&password=test123&name=꼬북이&email=test123@naver.com");
            // 보낼 데이터가 있으면 파라미터를 채운다.

            /**
             * 2. HttpURLConnection을 통해 web의 데이터를 가져온다.
             * */

                try {
                    URL url = new URL(_url);
                    urlConn = (HttpURLConnection) url.openConnection();

                    // [2-1]. urlConn 설정.
                    urlConn.setRequestMethod("POST"); // URL 요청에 대한 메소드 설정 : POST.
                    urlConn.setRequestProperty("Accept-Charset", "UTF-8"); // Accept-Charset 설정.
                    urlConn.setRequestProperty("Context_Type", "application/x-www-form-urlencoded;cahrset=UTF-8");

                    // [2-2]. parameter 전달 및 데이터 읽어오기.
                    String strParams = sbParams.toString(); //sbParams에 정리한 파라미터들을 스트링으로 저장. 예)id=id1&pw=123;
                    OutputStream os = urlConn.getOutputStream();
                    os.write(strParams.getBytes("UTF-8")); // 출력 스트림에 출력.
                    os.flush(); // 출력 스트림을 플러시(비운다)하고 버퍼링 된 모든 출력 바이트를 강제 실행.
                    os.close(); // 출력 스트림을 닫고 모든 시스템 자원을 해제.

                    // [2-3]. 연결 요청 확인.
                    // 실패 시 null을 리턴하고 메서드를 종료.
                    if (urlConn.getResponseCode() != HttpURLConnection.HTTP_OK)
                        return null;

                    // [2-4]. 읽어온 결과물 리턴.
                    // 요청한 URL의 출력물을 BufferedReader로 받는다.
                    BufferedReader reader = new BufferedReader(new InputStreamReader(urlConn.getInputStream(), "UTF-8"));

                    // 출력물의 라인과 그 합에 대한 변수.
                    String line;
                    String page = "";

                    // 라인을 받아와 합친다.
                    // 버퍼의 웹문서 소스를 줄 단위로 읽어(line), page에 저장함
                    while ((line = reader.readLine()) != null) {
                        page += line;
                    }

                    try {
                        // JSP에서 보낸 JSON 받아오자  JSONObject = siteDataMain
                        JSONObject json = new JSONObject(page);
                        JSONArray jArr = json.getJSONArray("naya");

                        // JSON이 가진 크기만큼 데이터를 받아옴
                        for (int i = 0; i < jArr.length(); i++) {
                            json = jArr.getJSONObject(i);
                            System.out.println(i + "번째 데이터 : " + json.getString("id"));
                            System.out.println(i + "번째 데이터 : " + json.getString("name"));
                            System.out.println(i + "번째 데이터 : " + json.getString("address"));
                            System.out.println(i + "번째 데이터 : " + json.getString("img"));
                            System.out.println("\n");

                            list.add(new CardForSite(json.getString("id"), json.getString("name"), json.getString("address"), json.getString("img")));
                        }

                        /* 여기까지 서버가 보낸 데이터를 받아 왔다. 밑에는 확인을 위한 수행 */

                        //String data = "받은 데이터 : " + getJsonData[0] + " " + getJsonData[1];
                        //setTextView(data);

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } catch (MalformedURLException e) { // for URL.
                    e.printStackTrace();
                } catch (IOException e) { // for openConnection().
                    e.printStackTrace();
                } finally {
                    if (urlConn != null)
                        urlConn.disconnect();
                }
            return null;
        }
    }

}

 

activity_main.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"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <!--
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <ImageView
            android:id="@+id/imageView2"
            android:layout_width="380dp"
            android:layout_height="80dp"
            android:scaleType="fitCenter"
            app:srcCompat="@drawable/main_coupang" />

    </LinearLayout>
-->

    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/recyclerView" />

</LinearLayout>

 

card_layout_main.xml

<?xml version="1.0" encoding="utf-8"?>


<androidx.recyclerview.widget.RecyclerView
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="450dp"
    card_view:cardCornerRadius="5dp"
    card_view:cardElevation="10dp"
    android:id="@+id/cardSiteCardView"
    android:layout_margin="5dp" >



    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="170dp"
            android:id="@+id/cardSiteImage"
            android:scaleType="centerCrop"/>

        <TextView
            android:textStyle="bold"
            android:id="@+id/cardSiteTitle"
            android:gravity="center_vertical"
            android:layout_width="match_parent"
            android:singleLine="true"
            android:ellipsize="end"
            android:layout_height="30dp" />

        <TextView
            android:ellipsize="end"
            android:id="@+id/cardSiteText"
            android:gravity="center_vertical"
            android:layout_width="match_parent"
            android:layout_height="50dp" />


    </LinearLayout>

</androidx.recyclerview.widget.RecyclerView>

 

RecycleAdaper.java

package com.example.mycoupang;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;

import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.daimajia.androidanimations.library.Techniques;
import com.daimajia.androidanimations.library.YoYo;
import com.squareup.picasso.Picasso;
import java.util.List;

/**
 * Created by riu on 2018-03-31.
 */

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

    private List<CardForSite> dataList;

    private int itemLayout;

    /**
     * 생성자
     *
     * @param items
     * @param itemLayout
     */

    public RecycleAdapter(List<CardForSite> items, int itemLayout) {
        System.out.println(">>>>> 000:");
        this.dataList = items;
        this.itemLayout = itemLayout;
    }

    /**
     * 레이아웃을 만들어서 Holer에 저장
     *
     * @param viewGroup
     * @param viewType
     * @return
     */

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        System.out.println(">>>>> 111:");
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(itemLayout, viewGroup, false);
        return new ViewHolder(view);
    }

    /**
     * listView getView 를 대체
     * <p>
     * 넘겨 받은 데이터를 화면에 출력하는 역할
     *
     * @param viewHolder
     * @param position
     */

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int position) {
        final CardForSite item = dataList.get(position);

        System.out.println(">>>>> 222:" + item.getSiteLink());


        viewHolder.cardSiteTitle.setText(item.getSiteTitle());
        viewHolder.cardSiteText.setText(item.getSitetext());
        viewHolder.cardSiteImage.setTag(item);

        // 애니메이션 FadeIn 카드뷰에 적용하기 위한
        YoYo.with(Techniques.FadeIn).playOn(viewHolder.cardSiteCardView);

        // 피카소를 이용하여 url 이미지 쉽게 하기 위한
        // gradle에 넣어야할 것도 있음
        String imageUrl = item.getSiteImage();
        Context context = viewHolder.cardSiteImage.getContext();
        Picasso.get()
                .load(imageUrl)
                .into(viewHolder.cardSiteImage);

        // 아이템뷰(하나의 카드뷰) 링크 클릭 리스너
        viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent it = new Intent(Intent.ACTION_VIEW, Uri.parse(item.getSiteLink()));
                view.getContext().startActivity(it);
            }
        });

    }

    @Override
    public int getItemCount() {
        System.out.println(">>>> getItemCount:"+dataList.size());
        return dataList.size();
    }

    /**
     * 뷰 재활용을 위한 viewHolder
     */

    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView cardSiteTitle;
        public TextView cardSiteText;
        public ImageView cardSiteImage;
        public CardView cardSiteCardView;

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

            // 카드뷰 애니메이션 처리를 위한
            cardSiteCardView = (CardView) itemView.findViewById(R.id.cardSiteCardView);
            cardSiteTitle = (TextView) itemView.findViewById(R.id.cardSiteTitle);
            cardSiteText = (TextView) itemView.findViewById(R.id.cardSiteText);
            cardSiteImage = (ImageView) itemView.findViewById(R.id.cardSiteImage);
        }
    }

}

 

CardForSite.java

package com.example.mycoupang;

public class CardForSite {
    private String siteTitle;
    private String siteLink;
    private String siteImage;
    private String sitetext;

    public CardForSite(String siteTitle, String siteLink, String sitetext, String siteImage) {
        this.siteTitle = siteTitle;
        this.siteLink = siteLink;
        this.siteImage = siteImage;
        this.sitetext = sitetext;
    }

    public String getSiteTitle() {
        return siteTitle;
    }

    public void setSiteTitle(String siteTitle) {
        this.siteTitle = siteTitle;
    }

    public String getSiteLink() {
        return siteLink;
    }

    public void setSiteLink(String siteLink) {
        this.siteLink = siteLink;
    }

    public String getSiteImage() {
        return siteImage;
    }

    public void setSiteImage(String siteImage) {
        this.siteImage = siteImage;
    }

    public String getSitetext() {
        return sitetext;
    }

    public void setSitetext(String sitetext) {
        this.sitetext = sitetext;
    }

}